OpenCV2 && Qt4 chapter2: Manipulating the Pixels

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章