在映像展開了以後, 很自然地我們想把映像的旋轉也做進來。我們找來了映像旋轉的公式:
X' = X cosθ - Y sinθ;
Y' = X sinθ + Y cosθ;
這個映像公式大家在高中數學課都是會算滴。 然後我們要擴充一下因為我們不是在原點做旋轉,我們要圍繞原來的圖片中心做旋轉, 那麼我們假定原來的映像中心是 oldCenterX, oldCenterY.旋轉完成以後, 我們要對映像位置坐調整,調整到新的座標中心, 那麼我們需要有個新的newCenterX, newCenterY;新的座標就是新的圖片的中心。那麼我們的公式就可以轉化成了:
X' = (X-oldCenterX) cosθ - (Y-oldCenterY) sinθ + newCenterX;
Y' = (X-oldCenterX) sinθ + (Y-oldCenterY) cosθ + newCenterY;
當然啦, 關鍵我們的問題不是旋轉後的位置,而是旋轉以後位置對於到原來的位置關係,也就是說我們更需要的是一個X,Y關於X'和Y'的運算式。很簡單的,我們把問題變成了2元一次方程!
X = Y'sinθ + X'cosθ + oldCenterY - newCenterX cosθ - newCenterY sinθ;
Y = Y'cosθ - X'sinθ + oldCenterY - newCenterY cosθ + newCenterX sinθ;
這樣要寫個合適的代碼就變得簡單了。 但是另一個顯著的問題就是沒有三角函數怎麼辦呢? 就像我們插值的時候用大數一樣, 我們用左移13位的大數來描述一下先,就像下面這樣的:
//test interface for math<br />const int K_CosineTable[24] =<br />{<br />8192,<br />8172,<br />8112,<br />8012,<br />7874,<br />7697,<br />7483,<br />7233,<br />6947,<br />6627,<br />6275,<br />5892,<br />5481,<br />5043,<br />4580,<br />4096,<br />3591,<br />3068,<br />2531,<br />1981,<br />1422,<br />856,<br />285,<br />-285<br />};<br />int ShiftCos(int y)<br />{<br />if (y<0) y*=-1;<br />y %= 360;<br />if ( y > 270 )<br />{<br />return ShiftCos((360 - y));<br />}<br />else if ( y > 180 )<br />{<br />return - ShiftCos((y - 180));<br />}<br />else if ( y > 90 )<br />{<br />return - ShiftCos((180 - y));<br />}<br />int index = (y >> 2);<br />int offset = (y % 4);<br />// on the borderline of overflowing if use JInt16<br />int cosVal = (4 - offset) * K_CosineTable[index]<br />+ offset * K_CosineTable[index + 1];<br />return cosVal >> 2;<br />}<br />int ShiftSin(int y)<br />{<br />return ShiftCos(y + 270);<br />}
有了這個三角函數的輔助:我們的最後的代碼就是這個樣子:
/**<br />** method to remove sharp the raw image with unsharp mask<br />* @param src input grayscale binary array<br />* @param srcWidth width of the input grayscale image<br />* @param srcHeight height of the input grayscale image<br />* @param [output] dst output gray-scale image.<br />* @param [output] dstWidth width of the output grayscale image<br />* @param [output] dstHeight height of the output grayscale image<br />* @param angle, rotate angle.<br />*/<br />void rotateImage (const unsigned char* src, int srcWidth, int srcHeight, unsigned char*& dst, int& dstWidth, int& dstHeight, int angle)<br />{</p><p>// first calculate the new width and height;<br />const int SHIFT = 13;<br />dstWidth = ( abs (srcWidth*ShiftCos(angle)) + abs (srcHeight*ShiftSin(angle))) >> SHIFT;<br />dstHeight = ( abs (srcWidth*ShiftSin(angle)) + abs (srcHeight*ShiftCos(angle))) >> SHIFT;<br />dst = new unsigned char [dstWidth*dstHeight];<br />int xcenter = srcWidth >> 1;<br />int ycenter = srcHeight >> 1;<br />int xnew = dstWidth >> 1;<br />int ynew = dstHeight >> 1;<br />const int xFix = ( xcenter <<8 ) - ((ynew * ShiftSin (angle)) >> 5 ) - ((xnew * ShiftCos (angle)) >> 5) ;<br />const int yFix = ( ycenter <<8 ) + ((xnew * ShiftSin (angle)) >> 5 ) - ((ynew * ShiftCos (angle)) >> 5) ;</p><p>int ox;<br />int oy;<br />int x;<br />int y;<br />int kx;<br />int ky;<br />int color [2][2];<br />for (int j=0;j<dstHeight;j++)<br />{<br />for (int i=0;i<dstWidth;i++)<br />{<br />ox = ((i * ShiftCos (angle) + j * ShiftSin (angle)) >> 5) + xFix;<br />oy = (((-1) * i * ShiftSin(angle) + j * ShiftCos (angle)) >> 5) + yFix;<br />if ( (ox >> 8) <= srcWidth && (ox >> 8) >=0 && (oy >> 8) <= srcHeight && (oy >> 8) >= 0)<br />{<br />kx = ox >> 8;<br />ky = oy >> 8;<br />x = ox & 0xFF;<br />y = oy & 0xFF;<br />color[0][0] = src[ ky*srcWidth + kx ];<br />color[1][0] = src[ ky*srcWidth + kx +1 ];<br />color[0][1] = src[ (ky+1)*srcWidth + kx ];<br />color[1][1] = src[ (ky+1)*srcWidth + kx+1 ];<br />int final = (0x100 - x)*(0x100 - y)*color[0][0] + x*(0x100 - y)*color[1][0] + (0x100-x)*y*color[0][1] + x*y*color[1][1];<br />final = final >> 16;<br />if (final>255)<br />final = 255;<br />if (final<0)<br />final = 0;<br />dst [ j*dstWidth + i] = (unsigned char)final;<br />}<br />else<br />{<br />dst [j*dstWidth + i] = 0xff;<br />}<br />}<br />}<br />}
這裡說明一下的是介面的定義,這裡的和目標灰階圖相關的參數都是參考型別的。表示都是輸出的參數,因為映像旋轉以後的大小會發生變化,函數外不是很方便事先分配好記憶體,所以這裡採用了就地分配的模式。記憶體配置在函數內部完成。雖然沒有用ticks去最後測速,但是想來沒有浮點數的計算,這裡的效率還是比較高的,當然這裡一些細節的記錄上還有可以再最佳化一下的,比如說這個常數5!!!Majic Number呵呵, 其實就是原來的那些數字都希望是左移8的, 所以三角函數中出來的數字需要左移5位!!除此以外就完全是公式的套用了 呵呵。
最後來點各個角度的看看:
20度
40度
60度
80度
100度
120度