Алгоритм уменьшения изображения - PullRequest
7 голосов
/ 05 марта 2012

Не могли бы вы помочь мне найти правильный алгоритм изменения размера изображения?У меня есть изображение номера.Максимальный размер 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 ]

10 голосов
/ 05 марта 2012

Общий подход заключается в фильтрации входных данных для генерации меньшего размера и порогового значения для преобразования в монохромный. Простейший фильтр для реализации - это простое среднее значение, и он часто дает хорошие результаты. Фильтр Sinc теоретически является лучшим, но его применение нецелесообразно и имеет вызывающие артефакты, которые часто нежелательны. Доступно много других фильтров, таких как Lanczos или Tent (что является обобщенной формой билинейного).

Вот версия среднего фильтра в сочетании с пороговым значением. Предполагая, что 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

3 голосов
/ 06 марта 2012

Я нашел реализацию билинейной интерполяции. С-код.

Предполагая, что:

a - указатель первичного массива (который нам нужно растянуть / сжать).

oldw - основная ширина

oldh - начальная высота

b - указатель вторичного массива (который мы получаем после сжатия / растяжения)

neww - вторичная ширина

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

Надеюсь, это будет полезно для других пользователей. Но тем не менее я все еще сомневаюсь, будет ли это работать в моей ситуации (когда не сгибает, а сжимает массив). Есть идеи?

3 голосов
/ 05 марта 2012

Поскольку вы можете использовать библиотеку, вы можете посмотреть привязки imagemagick C ++ .

Вы также можете вывести изображение в простом формате, например 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 голосов
/ 05 марта 2012

Если вы используете Win32, то функция StretchBlt, возможно, поможет.

Функция StretchBlt копирует растровое изображение из исходного прямоугольника в целевой прямоугольник, растягивая или сжимая растровое изображение, чтобы соответствовать размерам целевого прямоугольника, если необходимо. Система растягивает или сжимает растровое изображение в соответствии с режимом растяжения, установленным в настоящее время в контексте целевого устройства.

2 голосов
/ 05 марта 2012

Чтобы правильно уменьшить масштаб изображения, вы должны разделить его на квадратные блоки пикселей и затем использовать что-то вроде Билинейная интерполяция , чтобы найти правильный цвет пикселя, который должен заменить блок NxN пикселей, на которых выполняется интерполяция.

Поскольку я не очень хорош в математике, я не буду пытаться дать вам пример того, как код хотел бы. Извините: (

2 голосов
/ 05 марта 2012

Я думаю, вам нужно Интерполяция .Существует множество алгоритмов, например, вы можете использовать Билинейная интерполяция

0 голосов
/ 05 марта 2012

Один из способов уменьшения размера 200x200 изображения, скажем до 100x100, состоит в том, чтобы брать каждый 2-й пиксель вдоль каждой строки и столбца. Я оставлю вас накатить свой собственный код для сокращения до размера, который не является делителем исходного размера. И я не даю никаких гарантий относительно пригодности этого подхода для вашей проблемы.

...