减少颜色数目
来源:互联网 发布:软件测试shell脚本 编辑:程序博客网 时间:2024/05/18 01:43
预备知识:
如常见的RGB24图像有256×256×256中颜色,通过Reduce Color将每个通道的像素减少8倍至256/8=32种,则图像只有32×32×32种颜色。假设量化减少的倍数是N,则代码实现时就是简单的value/N*N,通常我们会再加上N/2以得到相邻的N的倍数的中间值,最后图像被量化为(256/N)×(256/N)×(256/N)种颜色。
data[i]是整数(假设原来是120),x/div得到的是商(1),余数被舍弃,再×div得到的是64,再加上div/2就是64+32=98。推广点我们可以想到64~127之间的数经过上述运算得到的都是98,其他区间的数可以依此类推。这样就起到了压缩色彩空间的作用。data[i]相当于*(data+i)“
版本一:void ReduceColor(Mat& image,int div){ int n1=image.rows;//行数 int n2=image.cols*image.channels();//每行的列数 for(int i=0;i<n1;i++){ uchar* data=image.ptr<uchar>(i);//每行的首地址,利用ptr for(int j=0;j<n2;j++) { data[j]=data[j]-data[j]%(div)+div/2; } }}int main(){ Mat image=imread("F:\\opencv_test\\14.jpg"); namedWindow("1"); imshow("1",image); //Mat image2=image.clone(); ReduceColor(image,64); namedWindow("2"); imshow("2",image); waitKey(0); return 0;}
其他的颜色缩减公式:
data[j]=data[j]/div*div+div/2;
*data++=*data/div*div+div/2;
版本二:通用版本,允许用户指定,输入和输出图像。
之前的例子,变换直接作用在输入图像上的,我们称之为In_place变换。这种方式,不需要额外的图像来保存输出的结果,可以节省一定的内存。但是一些情况下,用户不希望原始图像被改变。最简单创建一个图像深拷贝的方式是调用clone函数,Mat imageclone=image.clone();ReduceColor(imageclone); 实际中我们可以实现这一机制,当我们输入输出是同意变量时仍采用的是IN_place方式,否则用户必须提供一个Mat的实例;
代码当中首先用creat函数创建一个与输入图像的尺寸和类型相同的矩阵,注意creat函数创建的图像内存都是连续的,不会对图像的行进行填补,分配的内存大小为total()*elemSize()
#include<opencv2\opencv.hpp>using namespace cv;void ReduceColor(Mat& image,Mat& result,int div){ result.create(image.rows,image.cols,image.type()); int n1=image.rows;//行数 int n2=image.cols*image.channels();//每行的列数 for(int i=0;i<n1;i++){ uchar* data_out=result.ptr<uchar>(i); uchar* data=image.ptr<uchar>(i);//每行的首地址,利用ptr for(int j=0;j<n2;j++) { //data[j]=data[j]/div*div+div/2;与下面语句等价 //*data++=*data/div*div+div/2; data_out[j]=data[j]-data[j]%(div)+div/2; } }}int main(){ Mat result,image=imread("F:\\opencv_test\\14.jpg"); namedWindow("1"); imshow("1",image); //Mat image2=image.clone(); ReduceColor(image,result,64); namedWindow("2"); imshow("2",result); waitKey(0); return 0;}
版本三:高效遍历连续图像(这种方法最高效)
考虑到效率,图像可能会在行尾,扩大若干个像素;但是当不对图像进行行填补的时候,图像可以视为一个W*H的一维数组;可以通过Mat的一个成员函数isContinuous来判断这幅图像是否进行了填补,如果isContinuous返回值为真,说明没有对图像进行填补;因此,在一些图像中我们可以利用图像的连续性,把整个处理过程用一个循环完成;则上面的颜色缩减函数可以重写;
void ReduceColor(Mat& image,int div){ int rn=image.rows; int cn=image.cols*image.channels(); if(image.isContinuous()){ cn=rn*cn; rn=1; } for(int i=0;i<rn;i++){ uchar* data=image.ptr<uchar>(i); for(int j=0;j<cn;j++) { data[j]=data[j]/div*div+div/2; } }}
版本四:使用底层指针运算
在Mat中,图像数据以usigned char形式保存在一块内存中,这块内存的首地址可以通过data成员变量得到,其中data是一个usigned char的指针,则循环可以如下方式开始:
uchar* data=image.data;从当前一行到下一行可以用指针加上行宽得到,data+=image.step;其中step代表图像的行宽(包括填补的像素),则可以通过如下方式得到第j行、第i列元素的地址data=image.data+j*image.step+i*elemSize(); 建议不使用这种方式,容易出错,且不适应于带感兴趣区域(ROI)的图像;
void ReduceColor(Mat& image,int div){ int rn=image.rows; int cn=image.cols*image.channels(); uchar* data=image.data; for(int i=0;i<rn;i++){ for(int j=0;j<cn;j++) { data[j]=data[j]/div*div+div/2; } data=data+image.step; } }
版本五:使用迭代器遍历图像
迭代器的定义方式Mat_::iterator iter=image.begin();其中begin和end也需要用模板化的版本;因为这里使用的是彩色图像,所以返回类型是Vec3b,每个颜色分量可以使用[]得到;
void ReduceColor(Mat& image,int div){Mat_<Vec3b>::iterator iter=image.begin<Vec3b>();//Mat_<Vec3b>::iterator iter2=image.end<Vec3b>();while(iter!=image.end<Vec3b>())//也可以用for循环{ (*iter)[0]=(*iter)[0]/div*div+div/2; (*iter)[1]=(*iter)[1]/div*div+div/2; (*iter)[2]=(*iter)[2]/div*div+div/2; iter++;} }
版本六:使用图像坐标
void ReduceColor(Mat& image,int div){int rn=image.rows ;int cn=image.cols;if(image.channels()==1){ for(int i=0;i<rn;i++) for(int j=0;j<cn;j++) image.at<uchar>(i,j)=image.at<uchar>(i,j)/div*div+div/2;} else if(image.channels()==3){for(int i=0;i<rn;i++){ for(int j=0;j<cn;j++){ image.at<Vec3b>(i,j)[0]=image.at<Vec3b>(i,j)[0]/div*div+div/2; image.at<Vec3b>(i,j)[1]=image.at<Vec3b>(i,j)[1]/div*div+div/2; image.at<Vec3b>(i,j)[2]=image.at<Vec3b>(i,j)[2]/div*div+div/2; }}}}
计算函数的运行时间:
getTickCount()返回上次开机算起的时钟周期数,由于我们需要的是函数运行的时间,因此需要另外一个函数getTickFrequency(),该函数返回每秒内的周期数。注意,返回时间的单位是秒
int main(){ Mat result,image=imread("F:\\opencv_test\\14.jpg"); double dur; dur=static_cast<double>(getTickCount()); ReduceColor(image,64); dur=static_cast<double>(getTickCount())-dur;//static_cast进行强制类型转换,转成double形 dur=dur/getTickFrequency(); namedWindow("2"); imshow("2",image); cout<<dur<<endl; waitKey(0); return 0;}
- 减少颜色数目
- [Unity优化]减少顶点数目
- 杭电ACM1004 数目最多的颜色
- 在Unity3D中的渲染优化-减少需要处理的顶点数目
- 在Unity3D中的渲染优化-减少需要处理的片元数目
- 减少图像的颜色数量(除法运算)
- 使用指针ptr指针访问像素,并且减少颜色数量
- poj 2777 Count Color 【线段树lazy区间染色 + 查询区间颜色数目 + 状态压缩】
- 24位转8位位图,不是灰度,有颜色的,尽量减少失真
- 等值数目
- 等值数目
- 水池数目
- 等值数目
- 水池数目
- 水池数目
- 水池数目
- 等值数目
- 水池数目
- 160Intersection of Two Linked Lists
- opencv 打开摄像头却捕捉不到图像解决方法
- 算法学习之排序算法(二)(直接插入排序法)
- 补间动画
- 属性动画
- 减少颜色数目
- NOIP模拟8.12总结
- hdu 5360 Hiking (贪心+优先队列)
- JSP学习笔记(七):session
- hdu 2955
- 3DES、AES、RC6、TEA、RSA、MD5、SHA1、SHA256大聚齐
- Toeplitz 矩阵对角化
- 对话框合集
- HDU 5372 Segment Game (树状数组+离散化)