2013-03-02 55 views
10

我正在嘗試編寫一個基本的雙三次尺寸調整算法來調整24位RGB位圖的大小。我對the math有一個大致的瞭解,我使用谷歌代碼中的this implementation作爲指導。我在這裏沒有使用任何外部庫 - 我只是試驗算法本身。該位表示爲一個普通的std::vector<unsigned char>用於圖像縮放的雙立方插值算法

inline unsigned char getpixel(const std::vector<unsigned char>& in, 
    std::size_t src_width, std::size_t src_height, unsigned x, unsigned y, int channel) 
{ 
    if (x < src_width && y < src_height) 
     return in[(x * 3 * src_width) + (3 * y) + channel]; 

    return 0; 
} 

std::vector<unsigned char> bicubicresize(const std::vector<unsigned char>& in, 
    std::size_t src_width, std::size_t src_height, std::size_t dest_width, std::size_t dest_height) 
{ 
    std::vector<unsigned char> out(dest_width * dest_height * 3); 

    const float tx = float(src_width)/dest_width; 
    const float ty = float(src_height)/dest_height; 
    const int channels = 3; 
    const std::size_t row_stride = dest_width * channels; 

    unsigned char C[5] = { 0 }; 

    for (int i = 0; i < dest_height; ++i) 
    { 
     for (int j = 0; j < dest_width; ++j) 
     { 
      const int x = int(tx * j); 
      const int y = int(ty * i); 
      const float dx = tx * j - x; 
      const float dy = ty * i - y; 

      for (int k = 0; k < 3; ++k) 
      { 
       for (int jj = 0; jj < 4; ++jj) 
       { 
        const int z = y - 1 + jj; 
        unsigned char a0 = getpixel(in, src_width, src_height, z, x, k); 
        unsigned char d0 = getpixel(in, src_width, src_height, z, x - 1, k) - a0; 
        unsigned char d2 = getpixel(in, src_width, src_height, z, x + 1, k) - a0; 
        unsigned char d3 = getpixel(in, src_width, src_height, z, x + 2, k) - a0; 
        unsigned char a1 = -1.0/3 * d0 + d2 - 1.0/6 * d3; 
        unsigned char a2 = 1.0/2 * d0 + 1.0/2 * d2; 
        unsigned char a3 = -1.0/6 * d0 - 1.0/2 * d2 + 1.0/6 * d3; 
        C[jj] = a0 + a1 * dx + a2 * dx * dx + a3 * dx * dx * dx; 

        d0 = C[0] - C[1]; 
        d2 = C[2] - C[1]; 
        d3 = C[3] - C[1]; 
        a0 = C[1]; 
        a1 = -1.0/3 * d0 + d2 -1.0/6 * d3; 
        a2 = 1.0/2 * d0 + 1.0/2 * d2; 
        a3 = -1.0/6 * d0 - 1.0/2 * d2 + 1.0/6 * d3; 
        out[i * row_stride + j * channels + k] = a0 + a1 * dy + a2 * dy * dy + a3 * dy * dy * dy; 
       } 
      } 
     } 
    } 

    return out; 
} 

問題:當我用這個算法來縮減圖像,它的工作原理除了輸出圖像包含右側出於某種原因全黑的像素,給它已被「裁剪」的外觀。

實施例:

INPUT IMAGE:

enter image description here

輸出圖像:

enter image description here

問題回顧算法,我不明白爲什麼會發生這種情況。有沒有人在這裏看到這個缺陷?

+13

好吧,那基於輸出圖像上,它看起來** **正是像你只計算了平方值的輸出像素。這應該是一個足夠的線索,讓你自己去調試和診斷它... – 2013-03-02 16:54:28

回答

12

嘗試不交換寬度和高度。

for (int i = 0; i < dest_width; ++i) 
    { 
     for (int j = 0; j < dest_height; ++j) 
+0

以他的方式遍歷它是緩存友好的,它會執行得更快。與您的建議相反,這會導致大圖片緩存未命中。 – TheHube 2015-03-03 16:08:18

+2

問題出在索引的範圍內。而緩存未命中是緩存大小和編譯器設置相關... – 2015-03-04 14:25:14

0

你應該getpixel你應該索引數組使用切換xz當你調用getpixel和:

[(y * 3 * src_width) + (3 * x) + channel] 
2

我建議不使用此功能,因爲它是書面非常糟糕。你需要做兩個卷積:首先由X座標,然後由Y.在這個函數中,所有這些卷積都在同一時間進行,導致非常慢的工作。如果你看jj循環體,你可以注意到從「d0 = C [0] - C [1]開始的所有身體的第二部分;」可以移到jj循環之外,因爲只有這個循環的最後一次迭代纔會對out []數組生效(所有先前的迭代結果都會被覆蓋)。

+1

我同意。在我看來,這種實現在縮小圖像大小時無法正常工作。內部循環需要考慮覆蓋目標像素區域的所有源像素。這個實現只考慮一個4x4的像素網格。 我發現的圖像重採樣的最佳解釋是:http://entropymine.com/imageworsener/resample 關於雙三次採樣的具體細節:http://entropymine.com/imageworsener/bicubic – 2016-01-09 10:20:40

0

getpixel(in, src_width, src_height, z, x, k)

z mean horizontal offset 
x mean vertical offset 

所以只需要修補getpixel功能,下面是補丁代碼:

inline unsigned char getpixel(const std::vector<unsigned char>& in, 
    std::size_t src_width, std::size_t src_height, unsigned y, unsigned x, int channel) 
{ 
    if (x < src_width && y < src_height) 
     return in[(y * 3 * src_width) + (3 * x) + channel]; 

    return 0; 
} 

std::vector<unsigned char> bicubicresize(const std::vector<unsigned char>& in, 
    std::size_t src_width, std::size_t src_height, std::size_t dest_width, std::size_t dest_height) 
{ 
    std::vector<unsigned char> out(dest_width * dest_height * 3); 

    const float tx = float(src_width)/dest_width; 
    const float ty = float(src_height)/dest_height; 
    const int channels = 3; 
    const std::size_t row_stride = dest_width * channels; 

    unsigned char C[5] = { 0 }; 

    for (int i = 0; i < dest_height; ++i) 
    { 
     for (int j = 0; j < dest_width; ++j) 
     { 
      const int x = int(tx * j); 
      const int y = int(ty * i); 
      const float dx = tx * j - x; 
      const float dy = ty * i - y; 

      for (int k = 0; k < 3; ++k) 
      { 
       for (int jj = 0; jj < 4; ++jj) 
       { 
        const int z = y - 1 + jj; 
        unsigned char a0 = getpixel(in, src_width, src_height, z, x, k); 
        unsigned char d0 = getpixel(in, src_width, src_height, z, x - 1, k) - a0; 
        unsigned char d2 = getpixel(in, src_width, src_height, z, x + 1, k) - a0; 
        unsigned char d3 = getpixel(in, src_width, src_height, z, x + 2, k) - a0; 
        unsigned char a1 = -1.0/3 * d0 + d2 - 1.0/6 * d3; 
        unsigned char a2 = 1.0/2 * d0 + 1.0/2 * d2; 
        unsigned char a3 = -1.0/6 * d0 - 1.0/2 * d2 + 1.0/6 * d3; 
        C[jj] = a0 + a1 * dx + a2 * dx * dx + a3 * dx * dx * dx; 

        d0 = C[0] - C[1]; 
        d2 = C[2] - C[1]; 
        d3 = C[3] - C[1]; 
        a0 = C[1]; 
        a1 = -1.0/3 * d0 + d2 -1.0/6 * d3; 
        a2 = 1.0/2 * d0 + 1.0/2 * d2; 
        a3 = -1.0/6 * d0 - 1.0/2 * d2 + 1.0/6 * d3; 
        out[i * row_stride + j * channels + k] = a0 + a1 * dy + a2 * dy * dy + a3 * dy * dy * dy; 
       } 
      } 
     } 
    } 

    return out; 
}