2015-06-20 82 views
0

我想玩一些OpenCV,並想出了一個有趣的小場景工作。 (x,y),(x + 1,y)(x,y + 1)和(x + 1,y)的顏色值相加, y + 1))並將結果除以4得到平均顏色值。然後,我處理的下一組像素是(x + 2,y + 2),它有3個相鄰像素。有效的方式訪問opencv Mat元素

然後,我也希望能夠做一個類似的事情,但與9像素(與選定的座標作爲中心工作)。

最初我從高斯模糊類型掩模開始,但這不是我想要的結果。從這些計算中,我只想得到1個像素值。所以輸出圖像將是1/4或1/9的大小。所以現在我已經得到了它的工作,我已經從字面上寫出來的計算在for循環:

for (int i = 1; i < myImage.rows -1; i++) 
{ 
    b = 0; 
    for (int k = 1; k < myImage.cols -1; k++) 
    { 
     //9 pixel radius 
     Result.at<Vec3b>(a, b)[1] = (myImage.at<Vec3b>(i-1, k-1)[1]+myImage.at<Vec3b>(i-1, k)[1]+myImage.at<Vec3b>(i+1, k)[1] + myImage.at<Vec3b>(i, k)[1]+myImage.at<Vec3b>(i, k-1)[1]+myImage.at<Vec3b>(i, k+1)[1] + myImage.at<Vec3b>(i + 1, k+1)[1] + myImage.at<Vec3b>(i-1, k + 1)[1] + myImage.at<Vec3b>(i + 1, k - 1)[1])/9; 
     Result.at<Vec3b>(a, b)[2] = (myImage.at<Vec3b>(i-1, k-1)[2]+myImage.at<Vec3b>(i-1, k)[2]+myImage.at<Vec3b>(i+1, k)[2] + myImage.at<Vec3b>(i, k)[2]+myImage.at<Vec3b>(i, k-1)[2]+myImage.at<Vec3b>(i, k+1)[2] + myImage.at<Vec3b>(i + 1, k+1)[2] + myImage.at<Vec3b>(i-1, k + 1)[2] + myImage.at<Vec3b>(i + 1, k - 1)[2])/9; 
     Result.at<Vec3b>(a, b)[0] = (myImage.at<Vec3b>(i-1, k-1)[0]+myImage.at<Vec3b>(i-1, k)[0]+myImage.at<Vec3b>(i+1, k)[0] + myImage.at<Vec3b>(i, k)[0]+myImage.at<Vec3b>(i, k-1)[0]+myImage.at<Vec3b>(i, k+1)[0] + myImage.at<Vec3b>(i + 1, k+1)[0] + myImage.at<Vec3b>(i-1, k + 1)[0] + myImage.at<Vec3b>(i + 1, k - 1)[0])/9; 

     //4 pixel radius 
     //  Result.at<Vec3b>(a, b)[1] = (myImage.at<Vec3b>(i, k)[1] + myImage.at<Vec3b>(i + 1, k)[1] + myImage.at<Vec3b>(i, k + 1)[1] + myImage.at<Vec3b>(i, k - 1)[1] + myImage.at<Vec3b>(i - 1, k)[1])/5; 
     //  Result.at<Vec3b>(a, b)[2] = (myImage.at<Vec3b>(i, k)[2] + myImage.at<Vec3b>(i + 1, k)[2] + myImage.at<Vec3b>(i, k + 1)[2] + myImage.at<Vec3b>(i, k - 1)[2] + myImage.at<Vec3b>(i - 1, k)[2])/5; 
     //  Result.at<Vec3b>(a, b)[0] = (myImage.at<Vec3b>(i, k)[0] + myImage.at<Vec3b>(i + 1, k)[0] + myImage.at<Vec3b>(i, k + 1)[0] + myImage.at<Vec3b>(i, k - 1)[0] + myImage.at<Vec3b>(i - 1, k)[0])/5; 
     b++; 
    } 
    a++; 
} 

顯然,這是可以設置被稱爲兩個選項不同的功能,但我我只是想知道是否有更高效的方法來實現這一點,這會讓掩碼的大小發生改變。

感謝您的幫助!

+1

這聽起來像你只是談論與線性插值調整圖像大小。 – beaker

+1

您是否打算用opencv實現這種最高效的方式,或者您想學習如何使用opencv進行高效的像素訪問? – bendervader

+0

我的意圖主要是找到一種更容易的方法來改變用於創建輸出的像素的數量,而不僅僅是將其全部寫出來,這與opencv相關。它能很好地使用輸入算法。而不僅僅是一長串手寫代碼來完成一組不同的輸入。 – FreakShow

回答

0

我假設你想要做的這一切,沒有內置函數(如resizemean,或filter2d),只是想用at直接處理圖像。還有可以進一步的優化,但這是對原始代碼的合理和可理解的改進。

此外,應該指出的是,我忽略任何額外的行/列時,圖像大小不是由比例因子整除。如果你想要不同的東西,你需要指定預期的行爲。

我要做的第一件事就是改變你認爲的目標像素。假設你有一個3x3的鄰里,象這樣:

1 2 3 
4 5 6 
7 8 9 

我們將採取所有這些像素的平均值無論如何,所以無論我們稱之爲像素5目標或像素1,使生成的映像沒有什麼區別。我將調用像素1的目標,因爲它使數學更清晰。

1像素將永遠是比例因子整除座標。如果比例因子是2,那麼座標1將始終均勻。其次,不是在原始圖像尺寸上循環,這實際上導致重複計算Result中的相同像素,我將循環遍歷Result的尺寸並找出原始圖像中的哪些像素有助於結果中的每個像素。

因此,要找到結果圖像中對應於像素(x, y)的原始圖像中的鄰域,我們只需要查找該鄰域的像素1。由於它的比例因子的倍數,它只是

(x * scaleFactor, y * scaleFactor) 

最後,我們需要兩個嵌套循環在scaleFactor x scaleFactor窗口中添加循環。這是避免必須輸出這些長計算的部分。

在上面的3×3示例中,例如,在(x, y)附近像素9將是:

(x * scaleFactor + 2, y * scaleFactor + 2) 

我也直接做平均值計算在載體中而不是單獨做每個信道。這意味着我們的結果將溢出uchar,所以我使用Vec3i並在分割後將其重新設置爲Vec3b。這是您應該考慮使用內置函數mean來計算窗口平均值的一個地方,因爲它將消除這些新循環的需要。

所以,如果我們的原始圖像是myImage,我們有:

int scaleFactor = 3; 

Mat Result(myImage.rows/scaleFactor, myImage.rows/scaleFactor, 
      myImage.type(), Scalar::all(0)); 

for (int i = 0; i < Result.rows; i++) 
{ 
    for (int k = 0; k < Result.cols; k++) 
    { 
     // make sum an int vector so it can hold 
     // value = scaleFactor x scaleFactor x 255 
     Vec3i areaSum = Vec3i(0,0,0); 
     for (int m = 0; m < scaleFactor; m++) 
     { 
      for (int n = 0; n < scaleFactor; n++) 
      { 
       areaSum += myImage.at<Vec3b>(i*scaleFactor+m, k*scaleFactor+n); 
      } 
     } 

     Result.at<Vec3b>(i,k) = Vec3b(areaSum/(scaleFactor*scaleFactor)); 
    } 
} 

這裏有幾個樣本...

原文:

enter image description here

比例因子= 2 :

enter image description here

比例因子= 3:

enter image description here

比例因子= 5:

enter image description here