一、加入椒盐噪声------访问像素值
//加入椒盐噪声
void salt(Mat image,int n)//n为噪声数量
{
int i,j;
for(int k=0;k<n;++k)
{
i=rand()%image.cols;
j=rand()%image.rows;
if(image.channels()==1)//或image.type()==CV_8UC1
{
image.at<uchar>(j,i)=255;//image.at<Vec2b/Vec3f/Vec3i/Vec3d>最后一位表示数据类型:f浮点,d双精度浮点,b向量
}
else if(image.channels()==3)
{
for(int l=0;l<3;l++)image.at<Vec3b>(j,i)[l]=255;
}
}
}
访问像素值除了image.at<uchar>(i,j)外,还可以Mat_<uchar> im(image),然后im(i,j)=0;即可
二、减色-----用指针扫描图像
void colorReduce(const Mat &image,Mat &out,int div=64)
{
out.create(image.rows,image.cols,image.type());
int nl=image.rows;
int nc=image.cols*image.channels();//一般行的长度是4或8的整数倍,不够就填充,填充后的长度为有效宽度,可用step得到
for(int j=0;j<nl;++j)
{
const uchar* data_in=image.ptr<uchar>(j);//用指针方法,返回第j行地址
uchar* data_out=out.ptr<uchar>(j);
for(int i=0;i<nc;++i)
data_out[i]=data_in[i]/div*div+div/2;
}
}
1.其他减色方案:位运算uchar mask=0xFF<<n;如div=16,则mask=0xF0;
*data &=mask;
*data++ +=div>>1;
2.对连续图像的高速扫描,判断是否连续isContinuous,然后也可以借助image.reshape(1,1)修改维数,第一个参数表示新通道数,第二个参数为新的行数
void colorReduce2(const Mat &image,Mat &out,int div=64)
{
out.create(image.rows,image.cols,image.type());
int nl=image.rows;
int nc=image.cols*image.channels();//image.step==image.cols*image.elemSize()说明行没有填充
if(image.isContinuous())
{
nc*=nl;nl=1;//可以用reshape替换
}
for(int j=0;j<nl;++j)
{
const uchar* data_in=image.ptr<uchar>(j);//用指针方法,返回第j行地址
uchar* data_out=out.ptr<uchar>(j);
for(int i=0;i<nc;++i)
data_out[i]=data_in[i]/div*div+div/2;
}
}
3.低层次指针算法:uchar* data=image.data;//image.data返回内存块的第一个元素地址
data+=image.step;//下一行
不推荐使用
三、用迭代器扫描图像
void colorReduce3(const Mat &image,Mat &out,int div=64)
{
Mat_<Vec3b>::const_iterator it=image.begin<Vec3b>();//指向常对象要用常迭代器const_iterator
Mat_<Vec3b>::const_iterator itend=image.end<Vec3b>();
for(;it!=itend;++it)
{
for(int k=0;k<3;++k)(*it)[k]=(*it)[k]/div*div+div/2;
}
}
迭代器写法:1、Mat_<Vec3b>::iterator it;
2、MatIterator_<Vec3b> it;
若从第二行开始可以将迭代器写为:it=image.begin<Vec3b>()+image.cols;
四、高效的图像扫描循环
1、计算某代码段的运行时间
const int64 start=getTickCount();
colorReduce(image,image);
double duration=(getTickCount()-start)/getTickFrequency();
2、几种方法比较:
位运算最快
.at 很慢,扫描图片尽量不用,随机访问时用
迭代器的时间也比较长,只是简化过程,减少出错
五、扫描图像并访问相邻像素
void sharpen(const Mat &image,Mat &out)//图像锐化
{
out.create(image.size(),image.type());
int nchannels=image.channels();
for(int j=1;j<image.rows-1;++j)
{
const uchar* pre=image.ptr<uchar>(j-1);
const uchar* cur=image.ptr<uchar>(j);
const uchar* next=image.ptr<uchar>(j+1);
uchar* output=out.ptr<uchar>(j);
for(int i=nchannels;i<(image.cols-1)*nchannels;++i)
{
*output++=cv::saturate_cast<uchar>(5*cur[i]-cur[i+nchannels]-cur[i-nchannels]-pre[i]-next[i]);
//锐化计算后的值很可能超出范围(0~255),saturate_cast功能将小于0的置为0,大于255的置为255
}
}
out.row(0).setTo(Scalar(0,0,0));//单通道灰度图这里改为out.row(0).setTo(Scalar(0));
out.row(out.rows-1).setTo(Scalar(0,0,0));
out.col(0).setTo(Scalar(0,0,0));
out.col(out.cols-1).setTo(Scalar(0,0,0));
}
借助filter2D函数锐化:需要#include<opencv/cv.hpp>
void sharpen2D(const Mat &image,Mat &out)//图像锐化
{
//out.create(image.size(),image.type());
Mat kernel(3,3,CV_32F,Scalar(0));
kernel.at<float>(1,1)=5;
kernel.at<float>(0,1)=-1;
kernel.at<float>(1,0)=-1;
kernel.at<float>(2,1)=-1;
kernel.at<float>(1,2)=-1;
filter2D(image,out,image.depth(),kernel);
}
或者Mat kernel=(Mat_<char>(3,3)<<0,-1,0,-1,5,-1,0,-1,0);
六、简单的图像运算(必须有相同的大小和类型)
add(image,image2,out);//out[i]=image[i]+image2[i]
add(image,Scalar(0,200,0),out);//out[i]=image[i]+Scalar(k)
addWeighted(image,k1,image2,k2,k3,out);//out=k1*image+k2*image2+k3直接写这个效果相同
scaleAdd(image,k1,image2,out);//out=image*k1+image2
add(image,image2,out,mask)//if(mask[i])out[i]=image[i]+image2[i]
1、其他运算:subtract absdiff multiply divide (减,差的绝对值、乘、除)
位运算:bitwise_and bitwise_or bitwise_xor bitwise_not
最值:min max
sqrt pow abs cuberoot exp log
所有运算都会自动调用cv::saturate_cast
2、分割图像通道
vector<Mat>planes;//创建三通道向量
split(image,planes);//分割
planes[0]+=image2;//将蓝色通道加到image2上
merge(planes,out);//合并三通道
七、图像重映射
不修改图象值,而是把每个像素的位置重新映射到新的位置。可以创建图像特效或修正图像扭曲
void wave(const Mat &image,Mat &out)
{
Mat X(image.rows,image.cols,CV_32F);
Mat Y(image.rows,image.cols,CV_32F);
for(int i=0;i<image.rows;++i)
{
for(int j=0;j<image.cols;++j)
{
X.at<float>(i,j)=j;//保持的同一列
Y.at<float>(i,j)=i+15*sin(j/10.0);//第i行的像素,根据正弦曲线移动
}
}
remap(image,out,X,Y,INTER_LINEAR);
}
即目标图像(i,j)位置的像素值从原始图像的(Y.at<float>(i,j),X.at<float>(i,j))处寻找