【问题标题】:Image scaling and rotating in C/C++C/C++ 中的图像缩放和旋转
【发布时间】:2010-09-22 21:24:30
【问题描述】:

缩放二维图像阵列的最佳方法是什么?例如,假设我有一个 1024 x 2048 字节的图像,每个字节是一个像素。每个像素都是从 0 到 255 的灰度级别。我希望能够按任意因子缩放此图像并获得新图像。所以,如果我将图像缩放 0.68 倍,我应该得到一个大小为 0.68*1024 x 0.68*2048 的新图像。一些像素将相互折叠。而且,如果我按比例缩放 3.15 倍,我会得到一个更大的图像,其中像素被复制。那么,实现这一目标的最佳方法是什么?

接下来,我希望能够将图像旋转任意角度,范围为 0 到 360 度(0 - 2Pi)。旋转后裁剪图像不是问题。最好的方法是什么?

【问题讨论】:

    标签: c++ c image image-scaling


    【解决方案1】:

    有很多方法可以缩放和旋转图像。最简单的扩展方式是:

    dest[dx,dy] = src[dx*src_width/dest_width,dy*src_height/dest_height]
    

    但这会在增加尺寸时产生块状效果,而在减小尺寸时会丢失细节。有一些方法可以产生更好看的结果,例如,bilinear filtering

    对于旋转,可以使用a rotation matrix计算src像素位置:

    sx,sy = M(dx,dy)
    

    其中 M 是将目标像素映射到源图像的矩阵。同样,您需要进行插值以产生非块状结果。

    但如果您不想深入了解图像处理的数学知识,可以使用大量库。

    【讨论】:

      【解决方案2】:

      您正在做的是将一组输入点映射到一组输出点。问题的第一部分是确定调整大小或旋转的映射;第二部分是处理不完全位于像素边界上的点。

      调整大小的映射很容易:

      x' = x * (width' / width)
      y' = y * (height' / height)
      

      旋转映射只有一点点困难。

      x' = x * cos(a) + y * sin(a)
      y' = y * cos(a) - x * sin(a)
      

      确定网格外像素值的技术称为插值。有许多这样的算法,在速度和最终图像质量方面范围很广。其中一些按质量/时间递增的顺序是最近邻、双线性、双三次和 Sinc 滤波器。

      【讨论】:

        【解决方案3】:

        没有简单的方法可以做到这一点。 scaling 和旋转都不是微不足道的过程。

        因此建议使用 2d 成像库。 Magick++ 可以是一个想法,正如 divideandconquer.se 指出的那样,但还有其他想法。

        【讨论】:

          【解决方案4】:

          你想自己做脏活还是ImageMagick帮你做?

          【讨论】:

            【解决方案5】:

            复制或丢弃像素不是调整图像大小的最佳方法,因为结果会显示像素化和锯齿状。为获得最佳效果,您应该重新采样图像,这将使生成的图像看起来更平滑。重采样的方法有很多,比如双线性、双三次、lanczos等。

            看看 wxWidgets 中的ResampleBicubic 函数。它适用于各种图像,不仅是灰度图像,而且您应该能够根据自己的需要进行调整。然后还有重采样代码from VirtualDub。 Google Codesearch 可能会显示更多相关代码。

            编辑:链接在预览中看起来不错,但在发布时会损坏。这很奇怪。去谷歌代码搜索并分别查询“wxwidgets resamplebicubic”和“virtualdub resample”得到相同的结果。

            【讨论】:

              【解决方案6】:

              还没有提到,所以我要指出 OpenCV 具有缩放和旋转图像的功能,以及大量其他实用程序。 它可能包含许多与问题无关的功能,但它很容易设置并用于同类库。

              您可以尝试手动实现这样的转换,但简单的缩放和旋转方法通常会导致大量细节丢失。

              使用 OpenCV,可以像这样进行缩放:

              float scaleFactor = 0.68f;
              cv::Mat original = cv::imread(path);
              cv::Mat scaled;
              cv::resize(original, scaled, cv::Size(0, 0), scaleFactor, scaleFactor, cv::INTER_LANCZOS4);
              cv::imwrite("new_image.jpg", scaled);
              

              这会使用 Lanczos 插值将图像缩小 0.68 倍。

              我对旋转不太熟悉,但这里有一个示例,来自 OpenCV 网站上的一个教程,我已将其编辑到相关部分。 (原文也有歪曲和翻译...)

              /// Compute a rotation matrix with respect to the center of the image
              Point center = Point(original.size().width / 2, original.size().height / 2);
              double angle = -50.0;
              double scale = 0.6;
              
              /// Get the rotation matrix with the specifications above
              Mat rot_mat( 2, 3, CV_32FC1 );
              rot_mat = getRotationMatrix2D(center, angle, scale);
              
              /// Rotate the image
              Mat rotated_image;
              warpAffine(src, rotated_image, rot_mat, src.size());
              

              OpenCV Website

              They have some very nice documentation too.

              【讨论】:

              • 好点。我已经添加了一些示例,所以现在它更像是一个真正的答案。
              【解决方案7】:

              CxImage 是一个免费的图片处理库,可以为所欲为。 除了琐碎的东西我没有亲自使用它,但我看到它反复推荐。

              【讨论】:

              • 虽然理论上这可以回答这个问题,it would be preferable 在这里包含答案的基本部分,并提供链接以供参考。
              【解决方案8】:

              CxImage 调整大小的方法会产生奇怪的结果。我将 Resample 和 Resample2 函数与所有可用的插值方法变体一起使用,结果相同。例如,尝试将填充了白色的 1024 x 768 图像调整为 802 x 582 的大小。您会发现图像上的某些像素的颜色 不同于 为白色!你可以检查一下:在 Windows Paint 中打开调整大小的图像并尝试用黑色填充它。结果一定会让你很开心。

              【讨论】:

                【解决方案9】:

                查看Intel Performance Primitives。我以前使用过它,它在 x86 上产生了接近最佳的性能。还有一个测试程序可以让我们使用各种算法。

                【讨论】:

                • 虽然理论上这可以回答这个问题,it would be preferable 在这里包含答案的基本部分,并提供链接以供参考。
                【解决方案10】:
                point scaling(point p,float sx,float sy) {
                    point s;
                
                    int c[1][3];
                    int a[1][3]={p.x,p.y,1};
                    int b[3][3]={sx,0,0,0,sy,0,0,0,1};
                
                    multmat(a,b,c);
                
                    s.x=c[0][0];
                    s.y=c[0][1];
                
                    return s;
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-11-08
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多