14

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

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

4

10 回答 10

16

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

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

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

对于旋转,可以使用旋转矩阵计算 src 像素位置:

sx,sy = M(dx,dy)

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

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

于 2008-11-18T16:40:10.597 回答
11

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

调整大小的映射很容易:

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

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

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

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

于 2008-11-18T17:00:46.500 回答
9

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

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

于 2008-11-18T16:38:19.620 回答
8

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

于 2008-11-18T16:34:56.943 回答
4

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

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

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

于 2008-11-18T16:56:40.923 回答
3

它还没有被提及,所以我将指出 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 网站

他们也有一些非常好的文档。

于 2016-08-30T11:25:44.173 回答
2

CxImage是一个免费的图像处理库,它可以做你想做的事。除了琐碎的东西我没有亲自使用它,但我已经看到它反复推荐。

于 2008-11-18T17:08:37.017 回答
0

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

于 2010-08-19T14:21:29.163 回答
0

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

于 2012-02-15T17:48:20.617 回答
-1
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;
}
于 2009-08-04T15:19:59.863 回答