图像缩小算法

时间:2012-03-05 17:12:36

标签: c++ c algorithm image image-recognition

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

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

// 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];
                    }
            }

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

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

7 个答案:

答案 0 :(得分:10)

一般方法是过滤输入以生成较小的尺寸,并将阈值转换为单色。要实现的最简单的过滤器是简单的平均值,它通常会产生OK结果。 Sinc filter在理论上是最好的,但它实现起来是不切实际的,并且具有通常不期望的振铃伪像。还有许多其他过滤器,例如Lanczos或Tent(这是Bilinear的广义形式)。

这是一个与阈值相结合的平均过滤器版本。假设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;
    }
}

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

original nearest neighbor average+threshold

答案 1 :(得分:3)

由于你使用图书馆很好,你可以查看imagemagick C++ bindings

您也可以以pbm之类的简单格式输出图像,然后调用imagemagick命令调整其大小:

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

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

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

输出文件:

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 

答案 2 :(得分:3)

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

假设:

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

oldw - 主要宽度

oldh - 初级身高

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

neww - 辅助宽度

newh - seconday height


#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);       
    }
}
}

希望它对其他用户有用。但是,我仍然怀疑它是否会在我的情况下工作(当不是stratching,而是压缩数组)。有什么想法吗?

答案 3 :(得分:2)

我想,你需要Interpolation。有很多算法,例如你可以使用Bilinear interpolation

答案 4 :(得分:2)

要正确缩小图像尺寸,您应该将图像划分为方形像素块,然后使用类似Bilinear Interpolation的内容,以便找到应该替换NxN像素块的像素的正确颜色。重新进行插值。

由于我不太擅长数学,我不会试着给你一个代码如何的例子。对不起:(

答案 5 :(得分:2)

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

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

答案 6 :(得分:0)

200x200图片缩小到100x100的一种方法是沿每行和每列拍摄第2个像素。我将让您滚动自己的代码,缩小尺寸,而不是原始尺寸的除数。我不保证这种方法适用于您的问题。