最近事情好多呀,又是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 將幾個通道合成圖像