C++ 图像缩小算法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9570895/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 13:00:09  来源:igfitidea点击:

Image downscaling algorithm

c++calgorithmimageimage-recognition

提问by user1131662

Could you help me find the right algorithm for image resizing? I have an image of a number. The maximum size is 200x200, I need to get an image with size 15x15 or even less. The image is monochrome (black and white) and the result should be the same. That's the info about my task.

你能帮我找到合适的图像调整算法吗?我有一个数字的图像。最大尺寸为 200x200,我需要获得尺寸为 15x15 或更小的图像。图像是单色的(黑白),结果应该是一样的。这是关于我的任务的信息。

I've already tried one algorithm, here it is

我已经尝试了一种算法,这里是

// xscale, yscale - decrease/increase rate
for (int f = 0; f<=49; f++)
            {
                    for (int g = 0; g<=49; g++)//49+1 - final size
                    {
                            xpos = (int)f * xscale;
                            ypos = (int)g * yscale;
                            picture3[f][g]=picture4[xpos][ypos];
                    }
            }

But it won't work with the decrease of an image, which is my prior target. Could you help me find an algorithm, which could solve that problem (quality mustn't be perfect, the speed doesn't even matter). Some information about it would be perfect too considering the fact I'm a newbie. Of course, a short piece of c/c++ code (or a library) will be perfect too.

但它不会随着图像的减少而工作,这是我之前的目标。你能帮我找到一个可以解决这个问题的算法吗(质量不一定是完美的,速度甚至无关紧要)。考虑到我是新手,关于它的一些信息也很完美。当然,一小段 c/c++ 代码(或库)也将是完美的。

Edit: I've found an algorithm. Will it be suitable for compressing from 200 to 20?

编辑:我找到了一个算法。是否适合从 200 压缩到 20?

回答by Mark Ransom

The general approach is to filter the input to generate a smaller size, and threshold to convert to monochrome. The easiest filter to implement is a simple average, and it often produces OK results. The Sinc filteris theoretically the best but it's impractical to implement and has ringing artifacts which are often undesirable. Many other filters are available, such as Lanczosor Tent (which is the generalized form of Bilinear).

一般的做法是对输入进行过滤以生成较小的尺寸,并将阈值转换为单色。最容易实现的过滤器是一个简单的平均值,它通常会产生好的结果。该正弦滤波器在理论上是最好的,但它是不切实际的实施,并已振铃效应这往往是不可取的。许多其他过滤器可用,例如Lanczos或 Tent(这是双线性的广义形式)。

Here's a version of an average filter combined with thresholding. Assuming picture4is the input with pixel values of 0 or 1, and the output is picture3in the same format. I also assumed that xis the least significant dimension which is opposite to the usual mathematical notation, and opposite to the coordinates in your question.

这是一个结合阈值的平均滤波器版本。假设picture4是像素值为0或1的输入,输出picture3格式相同。我还假设x这是与通常的数学符号相反的最不重要的维度,并且与您问题中的坐标相反。

int thumbwidth = 15;
int thumbheight = 15;
double xscale = (thumbwidth+0.0) / width;
double yscale = (thumbheight+0.0) / height;
double threshold = 0.5 / (xscale * yscale);
double yend = 0.0;
for (int f = 0; f < thumbheight; f++) // y on output
{
    double ystart = yend;
    yend = (f + 1) / yscale;
    if (yend >= height) yend = height - 0.000001;
    double xend = 0.0;
    for (int g = 0; g < thumbwidth; g++) // x on output
    {
        double xstart = xend;
        xend = (g + 1) / xscale;
        if (xend >= width) xend = width - 0.000001;
        double sum = 0.0;
        for (int y = (int)ystart; y <= (int)yend; ++y)
        {
            double yportion = 1.0;
            if (y == (int)ystart) yportion -= ystart - y;
            if (y == (int)yend) yportion -= y+1 - yend;
            for (int x = (int)xstart; x <= (int)xend; ++x)
            {
                double xportion = 1.0;
                if (x == (int)xstart) xportion -= xstart - x;
                if (x == (int)xend) xportion -= x+1 - xend;
                sum += picture4[y][x] * yportion * xportion;
            }
        }
        picture3[f][g] = (sum > threshold) ? 1 : 0;
    }
}

I've now tested this code. Here's the input 200x200 image, followed by a nearest-neighbor reduction to 15x15 (done in Paint Shop Pro), followed by the results of this code. I'll leave you to decide which is more faithful to the original; the difference would be much more obvious if the original had some fine detail.

我现在已经测试了这段代码。这是输入的 200x200 图像,然后是最近邻缩小到 15x15(在 Paint Shop Pro 中完成),然后是此代码的结果。我会让你决定哪个更忠实于原作;如果原作有一些精细的细节,差异会更加明显。

originalnearest neighboraverage+threshold

原来的最近的邻居平均值+阈值

回答by user1131662

I've found an implementation of a bilinear interpolaton. C code.

我找到了双线性插值的实现。C 代码。

Assuming that:

假如说:

a - a primary array (which we need to stretch/compress) pointer.

a - 一个主数组(我们需要拉伸/压缩)指针。

oldw - primary width

oldw - 主要宽度

oldh - primary height

oldh - 主要高度

b - a secondary array (which we get after compressing/stretching) pointer

b - 一个辅助数组(我们在压缩/拉伸后得到)指针

neww - secondary width

neww - 次要宽度

newh - seconday height

newh - 次要高度



#include <stdio.h>
#include <math.h>
#include <sys/types.h>

void resample(void *a, void *b, int oldw, int oldh, int neww,  int newh)
{
int i;
int j;
int l;
int c;
float t;
float u;
float tmp;
float d1, d2, d3, d4;
u_int p1, p2, p3, p4; /* nearby pixels */
u_char red, green, blue;

for (i = 0; i < newh; i++) {
    for (j = 0; j < neww; j++) {

        tmp = (float) (i) / (float) (newh - 1) * (oldh - 1);
        l = (int) floor(tmp);
        if (l < 0) {
            l = 0;
        } else {
            if (l >= oldh - 1) {
                l = oldh - 2;
            }
        }

        u = tmp - l;
        tmp = (float) (j) / (float) (neww - 1) * (oldw - 1);
        c = (int) floor(tmp);
        if (c < 0) {
            c = 0;
        } else {
            if (c >= oldw - 1) {
                c = oldw - 2;
            }
        }
        t = tmp - c;

        /* coefficients */
        d1 = (1 - t) * (1 - u);
        d2 = t * (1 - u);
        d3 = t * u;
        d4 = (1 - t) * u;

        /* nearby pixels: a[i][j] */
        p1 = *((u_int*)a + (l * oldw) + c);
        p2 = *((u_int*)a + (l * oldw) + c + 1);
        p3 = *((u_int*)a + ((l + 1)* oldw) + c + 1);
        p4 = *((u_int*)a + ((l + 1)* oldw) + c);

        /* color components */
        blue = (u_char)p1 * d1 + (u_char)p2 * d2 + (u_char)p3 * d3 + (u_char)p4 * d4;
        green = (u_char)(p1 >> 8) * d1 + (u_char)(p2 >> 8) * d2 + (u_char)(p3 >> 8) * d3 + (u_char)(p4 >> 8) * d4;
        red = (u_char)(p1 >> 16) * d1 + (u_char)(p2 >> 16) * d2 + (u_char)(p3 >> 16) * d3 + (u_char)(p4 >> 16) * d4;

        /* new pixel R G B  */
        *((u_int*)b + (i * neww) + j) = (red << 16) | (green << 8) | (blue);       
    }
}
}

Hope it will be useful for other users. But nevertheless I still doubth whether it will work in my situation (when not stratching, but compressing an array). Any ideas?

希望它对其他用户有用。但尽管如此,我仍然怀疑它是否适用于我的情况(不分层,但压缩数组时)。有任何想法吗?

回答by strcat

Since you're fine with using a library, you could look into the imagemagick C++ bindings.

由于您可以使用库,因此您可以查看imagemagick C++ bindings

You could also output the image in a simple format like a pbm, and then call the imagemagick command to resize it:

你也可以像 a 这样的简单格式输出图像pbm,然后调用 imagemagick 命令来调整它的大小:

system("convert input.pbm -resize 10x10 -compress none output.pbm");

Sample output file (note: you don't need to use a new line for each row):

示例输出文件(注意:您不需要为每一行使用一个新行):

P1
20 20
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0
0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

The output file:

输出文件:

P1
10 10
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 1 1 0 1 1 0 
0 0 0 0 1 0 0 1 1 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 1 1 1 1 
1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 

回答by strcat

If you use Win32, then StretchBlt function possibly help.

如果您使用 Win32,那么 StretchBlt 函数可能会有所帮助。

The StretchBlt function copies a bitmap from a source rectangle into a destination rectangle, stretching or compressing the bitmap to fit the dimensions of the destination rectangle, if necessary. The system stretches or compresses the bitmap according to the stretching mode currently set in the destination device context.

StretchBlt 函数将位图从源矩形复制到目标矩形,必要时拉伸或压缩位图以适合目标矩形的尺寸。系统根据目标设备上下文中当前设置的拉伸模式对位图进行拉伸或压缩。

回答by hired777

I think, you need Interpolation. There are a lot of algorithms, for example you can use Bilinear interpolation

我认为,您需要Interpolation。有很多算法,例如你可以使用双线性插值

回答by Tony The Lion

To properly downscale an image, you should divide your image up into square blocks of pixels and then use something like Bilinear Interpolationin order to find the right color of the pixel that should replace the NxN block of pixels you're doing the interpolation on.

要正确缩小图像,您应该将图像分成方形像素块,然后使用双线性插值之类的方法来找到正确的像素颜色,该颜色应该替换您正在进行插值的 NxN 像素块。

Since I'm not so good at the math involved, I'm not going to try give you an example of how the code would like. Sorry :(

由于我不太擅长所涉及的数学,因此我不会尝试举一个示例来说明代码的效果。对不起 :(

回答by High Performance Mark

One approach to downsizing a 200x200image to, say 100x100, would be to take every 2nd pixel along each row and column. I'll leave you to roll your own code for downsizing to a size which is not a divisor of the original size. And I provide no warranty as to the suitability of this approach for your problem.

200x200图像缩小到例如 的一种方法100x100是沿每行和每列获取每第二个像素。我会让你滚动你自己的代码来缩小到不是原始大小的除数的大小。我不保证这种方法对您的问题的适用性。