最近事情好多呀,又是ACM省赛,又是两个大课程设计,又是线代挂科的重考。又是dota,又是看视频什么的。哈哈。忙里偷闲地看完了第二章。大概地记录一下:
OpenCV2:
1. copyTo 和 clone都是深复制。已经经过实验。
cv::Mat_<Tp> 是cv::Mat的子类,并且是模板类,定义时就必须指定类型。挺好用的~~
2. 像素点的访问 (盐椒噪声的模拟)
用 cv::Mat 的at<Tp>函数或者用 cv::Mat_<Tp>的()函数。 两个访问得到的都是像素点,像素点是一个vector对象,里面放着各通道的值。 两个函数都要知道图像的类型。
void salt(cv::Mat &img,int n) //img是浅复制哟。。不过加上&引用更加好理解
{
int i,j;
qsrand(0);
while(n--)
{
i = qrand()% img.rows;
j= qrand()% img.cols;
if(img.channels() == 3)
{
// cv::Vec3b a vector of 3 uchar. color image type.
// typedef Vec<uchar, 3> Vec3b; Vec是模板类 channels = cn。 通道为3
// template<typename _Tp> _Tp& at
img.at<cv::Vec3b>(i,j)[0]=255;
img.at<cv::Vec3b>(i,j)[1]=255;
img.at<cv::Vec3b>(i,j)[2]=255;
}
}
}
3. 像素点遍历。 (图像的压缩)
图像的压缩算法: 通道值 t=t/div*div+div/2; 好神奇,不明白。反正就是能压缩。
1>用指针访问通道。
void colorReduce(cv::Mat &img1,int div=64)
{
int i,j;
uchar *p;
for(i=0;i<img1.rows;++i)
{
p = img1.ptr<uchar>(i);
// 一个像素点应该是 长度为3的vec结构~
for(j=0;j<img1.cols*img1.channels();++j)
p[j] = p[j]/div*div+div/2;
}
}
2>用迭代器访问像素。
void fun(cv::Mat &image,int div=64)
{
// 也可以 cv::Mat_<cv::Vec3b>::iterator it;
cv::MatIterator_<cv::Vec3b> it=image.begin<cv::Vec3b>();
cv::MatIterator_<cv::Vec3b> itend=image.end<cv::Vec3b>();
while(it != itend)
{
(*it)[0]= (*it)[0]/div*div+div/2;
(*it)[1]= (*it)[1]/div*div+div/2;
(*it)[2]= (*it)[2]/div*div+div/2;
++it;
}
}
最效率:
最快的遍历+压缩: 遍历用指针,压缩用位运算。image=(image&cv::Scalar(mask,mask,mask))+cv::Scalar(div/2,div/2,div/2);
4.相邻像素点处理。(图像锐化)
锐化算法: sharpen_pixel = 5*cur-left-right-up-down
1>指针访问通道:
只能处理单通道的。多通道的麻烦。
2> cv::filter2D函数。 矩阵过滤处理函数。 可以处理多通道的图像。 kernel矩阵要做好初始化。
void fun(cv::Mat &image,cv::Mat &res)
{
/*
// 只能是处理黑白图像,单通道型的。
res.create(image.size(),image.type());
for(int i=1;i<image.rows-1;++i)
{
uchar *pre=image.ptr<uchar>(i-1);
uchar *cur=image.ptr<uchar>(i);
uchar *next=image.ptr<uchar>(i+1);
uchar *output=res.ptr(i);
for(int j=1;j<image.cols*image.channels();++j)
{
// saturate_cast 饱和转换,限定范围在0-255
output[j] = cv::saturate_cast<uchar>( 5*(cur[j]) -cur[j]-pre[j]-next[j]-cur[j-1]-cur[j+1]);
}
}
/ */
// 通用的矩阵处理,多通道也是OK的哟。
cv::Mat kernel(3,3,CV_32F,cv::Scalar(0));
kernel.at<float>(1,1)=5.0;
kernel.at<float>(0,1)=-1.0;
kernel.at<float>(2,1)=-1.0;
kernel.at<float>(1,0)=-1.0;
kernel.at<float>(1,2)=-1.0;
cv::filter2D(image,res,image.depth(),kernel);
}
5. 图像与图像间的数学运算 (加减乘除都有,以加法为例。 图像添加水印)
1>除了copyTo,其它函数均要求 图像的size相同。 如果不同,可以如下解决:
cv::Mat image2 = image1( cv::Rect(0,0,logo.cols,logo.rows) ); 这样子 image2的size就和logo相同,且位置在image的左上角上,注意是浅复制哟。
2. 几个函数的说明。
a[i] b[i] c[i] 必须是一样大小。
// c[i]= a[i]+b[i];
cv::add(imageA,imageB,resultC);
// c[i]= a[i]+k;
cv::add(imageA,cv::Scalar(k),resultC);
// c[i]= k1*a[1]+k2*b[i]+k3;
cv::addWeighted(imageA,k1,imageB,k2,k3,resultC);
// c[i]= k*a[1]+b[i];
cv::scaleAdd(imageA,k,imageB,resultC);
// if (mask[i]) c[i]= a[i]+b[i];
cv::add(imageA,imageB,resultC,mask);
mask必须是单通道的。
void fun_arithmetic(cv::Mat &image1,cv::Mat &image2)
{
// 出错,图像要求同样的size..
//cv::add(image2,image1,image1);
cv::Mat tmp = image1(cv::Rect(10,10,image2.cols,image2.rows));
// image2.copyTo(tmp);
// 以下函数会用到 cv::saturate_cast 数值是安全的。
// cv::addWeighted(tmp,0.6,image2,0.4,0,tmp); 这个跟下一个重载了的函数效果相同。
tmp = 0.6*tmp + image2*0.4;
}
6. 通道的分离与合并。
split 将图像分离成各个通道
merge 将几个通道合成图像