2013-07-14 82 views
3

我一直在嘗試爲內存中的位圖編碼Bi-Cubic調整大小算法。我很熟悉bi-cubic插值是如何工作的,我已經使用了Wikipedia articleexisting implementations作爲編碼我自己版本的指南。Bi-Cubic調整大小的實現

以下是我的簡單實施。這裏,bmap是包含位圖數據的vector,並且get_subpixel僅僅是將位圖視爲由X x Y x Channel像素組成的3D陣列的簡單函數,並且返回指定座標處的單個子像素。

std::vector<unsigned char> bicubic_resize(
    std::vector<unsigned char>& bmap, std::size_t bmap_width, std::size_t bmap_height, 
    std::size_t channels, std::size_t dest_width, std::size_t dest_height) 
{ 
    std::vector<unsigned char> out(dest_width * dest_height * 3); 

    const double tx = double(bmap_width)/dest_width; 
    const double ty = double(bmap_height)/dest_height; 
    const std::size_t row_stride = dest_width * channels; 
    unsigned char C[5] = { 0 }; 

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

      for (int k = 0; k < 3; ++k) 
      { 
       for (int jj = 0; jj < 4; ++jj) 
       { 
        const int idx = y - 1 + jj; 
        unsigned char a0 = get_subpixel(bmap, idx, x, k); 
        unsigned char d0 = get_subpixel(bmap, idx, x - 1, k) - a0; 
        unsigned char d2 = get_subpixel(bmap, idx, x + 1, k) - a0; 
        unsigned char d3 = get_subpixel(bmap, idx, 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; 
} 

此代碼完美適用於某些目標尺寸。例如,如果原來的位圖500 X 366,而目標尺寸爲250 x 183,該算法完美的作品:

原文:
enter image description here
調整大小
enter image description here

Howev呃,對於某些其他目的的尺寸,例如100 x 73,目標圖像失真:
enter image description here

我已經渡過了插值代碼,我不能看到我在做什麼錯誤。

我會很感激任何提示,建議或答案。

+0

它看起來喜歡的顏色變得更亮更小的輸出圖像,並且最終數值溢出,並在第三個圖像中出現失真。即使是第一個調整大小的版本,也比原來的版本輕得多。 – Joni

+0

嗨,我想重新調整我的JPG和PNG。你的代碼適合我嗎? – Bhadresh

+0

請給我提示如何寫矢量的新縮放圖像對象。在此先感謝 – Bhadresh

回答

3

除了混合浮點和整數算術,我懷疑你的數值溢出/下溢與你的一些中間值。

一個簡單的解決方法是僅僅是一致的,並使用浮動整個點。現在您有:

unsigned char C[5] = { 0 }; 

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

     for (int k = 0; k < 3; ++k) 
     { 
      for (int jj = 0; jj < 4; ++jj) 
      { 
       const int idx = y - 1 + jj; 
       unsigned char a0 = get_subpixel(bmap, idx, x, k); 
       unsigned char d0 = get_subpixel(bmap, idx, x - 1, k) - a0; 
       unsigned char d2 = get_subpixel(bmap, idx, x + 1, k) - a0; 
       unsigned char d3 = get_subpixel(bmap, idx, 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; 
      } 
     } 
    } 
} 

你的unsigned charintdouble的混合物。這些1.0/3中的每一個都將您的8位數據轉換爲雙精度浮點數,然後賦值將其截斷回去。

相反,爲什麼不直接使用float

float C[5] = { 0 }; 

for (unsigned i = 0; i < dest_height; ++i) 
{ 
    for (unsigned j = 0; j < dest_width; ++j) 
    { 
     const float x = float(tx * j); 
     const float y = float(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 idx = y - 1 + jj; 
       float a0 = get_subpixel(bmap, idx, x, k); 
       float d0 = get_subpixel(bmap, idx, x - 1, k) - a0; 
       float d2 = get_subpixel(bmap, idx, x + 1, k) - a0; 
       float d3 = get_subpixel(bmap, idx, x + 2, k) - a0; 
       float a1 = -(1.0f/3.0f) * d0 + d2 - (1.0f/6.0f) * d3; 
       float a2 =   0.5f * d0 +    0.5f * d2; 
       float a3 = -(1.0f/6.0f) * d0 - 0.5f * d2 + (1.0f/6.0f) * 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.0f/3.0f) * d0 + d2 -(1.0f/6.0f) * d3; 
       a2 =   0.5f * d0 +    0.5f * d2; 
       a3 = -(1.0f/6.0f) * d0 - 0.5f * d2 + (1.0f/6.0f) * d3; 
       out[i * row_stride + j * channels + k] = saturate(a0 + a1 * dy + a2 * dy * dy + a3 * dy * dy * dy); 
      } 
     } 
    } 
} 

然後定義一個函數saturate做這個:

inline unsigned char saturate(float x) 
{ 
    return x > 255.0f ? 255 
     : x < 0.0f ? 0 
     :    unsigned char(x); 
} 

這將解決您的溢出問題,給你更好的精度和可能更好的性能了。

如果您需要進一步提高性能,那麼您應該看看定點算術。但現在,我認爲上述實施情況會更好。

而且,另外一個想法:你應該能夠通過預先計算dx * dxdx * dx * dx得到一些進一步提高效率,等等:

float C[5] = { 0 }; 

for (unsigned i = 0; i < dest_height; ++i) 
{ 
    for (unsigned j = 0; j < dest_width; ++j) 
    { 
     const float x = float(tx * j); 
     const float y = float(ty * i); 
     const float dx = tx * j - x, dx2 = dx * dx, dx3 = dx2 * dx; 
     const float dy = ty * i - y, dy2 = dy * dy, dy3 = dy2 * dy; 

     for (int k = 0; k < 3; ++k) 
     { 
      for (int jj = 0; jj < 4; ++jj) 
      { 
       const int idx = y - 1 + jj; 
       float a0 = get_subpixel(bmap, idx, x, k); 
       float d0 = get_subpixel(bmap, idx, x - 1, k) - a0; 
       float d2 = get_subpixel(bmap, idx, x + 1, k) - a0; 
       float d3 = get_subpixel(bmap, idx, x + 2, k) - a0; 
       float a1 = -(1.0f/3.0f) * d0 + d2 - (1.0f/6.0f) * d3; 
       float a2 =   0.5f * d0 +    0.5f * d2; 
       float a3 = -(1.0f/6.0f) * d0 - 0.5f * d2 + (1.0f/6.0f) * d3; 
       C[jj] = a0 + a1 * dx + a2 * dx2 + a3 * dx3; 

       d0 = C[0] - C[1]; 
       d2 = C[2] - C[1]; 
       d3 = C[3] - C[1]; 
       a0 = C[1]; 
       a1 = -(1.0f/3.0f) * d0 + d2 -(1.0f/6.0f) * d3; 
       a2 =   0.5f * d0 +    0.5f * d2; 
       a3 = -(1.0f/6.0f) * d0 - 0.5f * d2 + (1.0f/6.0f) * d3; 
       out[i * row_stride + j * channels + k] = saturate(a0 + a1 * dy + a2 * dy2 + a3 * dy3); 
      } 
     } 
    } 
} 
+0

你會考慮添加一個get_subpixel()的實現,這樣就完成了嗎? – dicroce

+0

將x和y轉換爲浮動max max dx,dx2,dx3,dy,dy2,dy3全爲零,我想這不是這裏的主意...... – fschmitt