分享

双线性插值(Bilinear interpolation)的图像旋转在mobile上面的C++实现

 学海无涯GL 2012-09-10

在图像拉伸了以后, 很自然地我们想把图像的旋转也做进来。我们找来了图像旋转的公式:

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位的大数来描述一下先,就像下面这样的:

  1. //test interface for math
  2. const int K_CosineTable[24] =
  3. {
  4. 8192,
  5. 8172,
  6. 8112,
  7. 8012,
  8. 7874,
  9. 7697,
  10. 7483,
  11. 7233,
  12. 6947,
  13. 6627,
  14. 6275,
  15. 5892,
  16. 5481,
  17. 5043,
  18. 4580,
  19. 4096,
  20. 3591,
  21. 3068,
  22. 2531,
  23. 1981,
  24. 1422,
  25. 856,
  26. 285,
  27. -285
  28. };
  29. int ShiftCos(int y)
  30. {
  31. if (y<0) y*=-1;
  32. y %= 360;
  33. if ( y > 270 )
  34. {
  35. return ShiftCos((360 - y));
  36. }
  37. else if ( y > 180 )
  38. {
  39. return - ShiftCos((y - 180));
  40. }
  41. else if ( y > 90 )
  42. {
  43. return - ShiftCos((180 - y));
  44. }
  45. int index = (y >> 2);
  46. int offset = (y % 4);
  47. // on the borderline of overflowing if use JInt16
  48. int cosVal = (4 - offset) * K_CosineTable[index]
  49. + offset * K_CosineTable[index + 1];
  50. return cosVal >> 2;
  51. }
  52. int ShiftSin(int y)
  53. {
  54. return ShiftCos(y + 270);
  55. }

有了这个三角函数的辅助:我们的最后的代码就是这个样子:

  1. /**
  2. ** method to remove sharp the raw image with unsharp mask
  3. * @param src input grayscale binary array
  4. * @param srcWidth width of the input grayscale image
  5. * @param srcHeight height of the input grayscale image
  6. * @param [output] dst output gray-scale image.
  7. * @param [output] dstWidth width of the output grayscale image
  8. * @param [output] dstHeight height of the output grayscale image
  9. * @param angle, rotate angle.
  10. */
  11. void rotateImage (const unsigned char* src, int srcWidth, int srcHeight, unsigned char*& dst, int& dstWidth, int& dstHeight, int angle)
  12. {
  13. // first calculate the new width and height;
  14. const int SHIFT = 13;
  15. dstWidth = ( abs (srcWidth*ShiftCos(angle)) + abs (srcHeight*ShiftSin(angle))) >> SHIFT;
  16. dstHeight = ( abs (srcWidth*ShiftSin(angle)) + abs (srcHeight*ShiftCos(angle))) >> SHIFT;
  17. dst = new unsigned char [dstWidth*dstHeight];
  18. int xcenter = srcWidth >> 1;
  19. int ycenter = srcHeight >> 1;
  20. int xnew = dstWidth >> 1;
  21. int ynew = dstHeight >> 1;
  22. const int xFix = ( xcenter <<8 ) - ((ynew * ShiftSin (angle)) >> 5 ) - ((xnew * ShiftCos (angle)) >> 5) ;
  23. const int yFix = ( ycenter <<8 ) + ((xnew * ShiftSin (angle)) >> 5 ) - ((ynew * ShiftCos (angle)) >> 5) ;
  24. int ox;
  25. int oy;
  26. int x;
  27. int y;
  28. int kx;
  29. int ky;
  30. int color [2][2];
  31. for (int j=0;j<dstHeight;j++)
  32. {
  33. for (int i=0;i<dstWidth;i++)
  34. {
  35. ox = ((i * ShiftCos (angle) + j * ShiftSin (angle)) >> 5) + xFix;
  36. oy = (((-1) * i * ShiftSin(angle) + j * ShiftCos (angle)) >> 5) + yFix;
  37. if ( (ox >> 8) <= srcWidth && (ox >> 8) >=0 && (oy >> 8) <= srcHeight && (oy >> 8) >= 0)
  38. {
  39. kx = ox >> 8;
  40. ky = oy >> 8;
  41. x = ox & 0xFF;
  42. y = oy & 0xFF;
  43. color[0][0] = src[ ky*srcWidth + kx ];
  44. color[1][0] = src[ ky*srcWidth + kx +1 ];
  45. color[0][1] = src[ (ky+1)*srcWidth + kx ];
  46. color[1][1] = src[ (ky+1)*srcWidth + kx+1 ];
  47. 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];
  48. final = final >> 16;
  49. if (final>255)
  50. final = 255;
  51. if (final<0)
  52. final = 0;
  53. dst [ j*dstWidth + i] = (unsigned char)final;
  54. }
  55. else
  56. {
  57. dst [j*dstWidth + i] = 0xff;
  58. }
  59. }
  60. }
  61. }

这里说明一下的是接口的定义,这里的和目标灰度图相关的参数都是引用类型的。表示都是输出的参数,因为图像旋转以后的大小会发生变化,函数外不是很方便事先分配好内存,所以这里采用了就地分配的模式。内存分配在函数内部完成。虽然没有用ticks去最后测速,但是想来没有浮点数的计算,这里的效率还是比较高的,当然这里一些细节的记录上还有可以再优化一下的,比如说这个常数5!!!Majic Number呵呵, 其实就是原来的那些数字都希望是左移8的, 所以三角函数中出来的数字需要左移5位!!除此以外就完全是公式的套用了 呵呵。

最后来点各个角度的效果图看看:

20度

20

40度

40

60度

60

80度

80

100度

100

120度

120

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约