2016-07-05 5 views
1

Я пытаюсь сделать программу, которая применяет средний фильтр к изображениям, и я думаю, что я близок к тому, чтобы делать это правильно, но все еще есть небольшие недостатки в изображениях. Например:Небольшие дефекты цвета при приближении к среднему фильтру на изображениях

Оригинал гонки: http://s72.photobucket.com/user/john_smith140/media/gp4_zpstafhejk5.jpg.html?filters[user]=139318132&filters[recent]=1&sort=1&o=2

Оригинальные Треугольники: http://s72.photobucket.com/user/john_smith140/media/input_zpsz2cfhrc7.jpeg.html?filters[user]=139318132&filters[recent]=1&sort=1&o=3

Модифицированный гонки: http://s72.photobucket.com/user/john_smith140/media/racing_zpsmzmawjml.jpeg.html?filters[user]=139318132&filters[recent]=1&sort=1&o=0

Modififed треугольники: http://s72.photobucket.com/user/john_smith140/media/triangles_zpsaretjfid.jpeg.html?filters[user]=139318132&filters[recent]=1&sort=1&o=1

черный фон белые точки, оригинальные: http://s72.photobucket.com/user/john_smith140/media/black%20background%20white%20dots_zpsuofaagnl.jpg.html?sort=3&o=2

черный фон белые точки, то же массив: http://s72.photobucket.com/user/john_smith140/media/one%20array_zpswteno2eb.jpg.html?sort=3&o=1

черный фон белые точки, различные массивы: http://s72.photobucket.com/user/john_smith140/media/two%20array_zpskbyjg97o.jpg.html?sort=3&o=0

я могу думать в двух причин недостатков. Один сам алгоритм, а другой в процессе преобразования char в float, а затем снова плавает на char.

Char to float conversion необходимо потому, что функция чтения ifstream считывает char, а затем мне нужно умножить каждую на 1/9, поэтому она должна быть плавающей точкой. Затем преобразуйте обратно в char, чтобы функция записи могла записать его обратно.

Некоторые объяснения об алгоритме. Я начинаю вычислять значение цвета из второго пикселя второй строки, а затем продолжается до второго последнего пикселя второй последней строки. Это потому, что я использую ядро ​​3x3, поэтому я не выхожу за пределы изображения (и так массива char, в котором я его сохранил). Для изображения 1024x768 он будет иметь размер 1024x768 * 3 (3 цветовых компонента). Таким образом, он начинается с позиции: bitsPerPixel * image_width + bitsPerPixel или 3 * 1024 + 3 = 4099, 2 ° пиксель строки 2 °. Затем он рассчитает среднее значение до последнего последнего пикселя 2 ° последней строки, который должен быть: imageSize - row_size - bitsPerPixel или (1024 * 768-3) - 1024 * 3 - 3. В интервале он рассчитает значение каждой позиции в массиве символов, что означает, что значение каждого цветового канала пикселя будет рассчитываться по цветному каналу окружающих пикселей. Вот код:

int size2 = bpp*width; 
float a1_9 = (1.0f/9.0f); 
float tmp; 
for (int i=size2+bpp; i<size-size2-bpp; i++) { 
tmp = a1_9 * ((float) image [i-size2-bpp] + (float) image [i-size2] + (float) image [i-size2+bpp] + (float) image [i-bpp] + (float) image [i] + (float) image [i+bpp] + (float)image [i+size2-bpp] + (float) image [i+size2] + (float) image [i+size2+bpp]); 
    image [i] = char (tmp); 
    float temp = (float) image [i]; 
} 

Я напечатал значение для одного взаимодействия скриншота гоночного автомобиля, что соответствует значениям позиции одного миллиона и получил это:

Image values are: -56 -57 -57 9 -43 -41 108 108 109 
tmp it is: 8.88889 
temp it is: 8 

Значения кажется о прямо на первый взгляд (выполняя средний уровень), поэтому у меня нет большой идеи о том, что происходит не так. Любая помощь будет оценена.

+0

Я не вижу разницы между несовершенна и оригиналом. –

+0

Вы можете попробовать использовать 'unsigned char' вместо' char' всюду. Совет для ленивых людей: используйте 'typedef unsigned char byte;'. Также вы не говорите, если 'image' является массивом' char' или 'unsigned char', и это может иметь значение ... – rodrigo

+0

Rubinson, есть много цветовых недостатков. В гоночных партиях фиолетовых недостатков и в треугольниках круглые линии разных цветов вырезают части треугольников – user2752471

ответ

2

2 Мысли о вашем алгоритме:

1.) Вы используете RGB цветовое пространство? Тогда почему ваши значения изображения отрицательные? Также вам не нужно конвертировать в float. Просто добавьте Целочисленные значения и разделите их на 9. Это намного быстрее, и в конце концов вы отбрасываете его обратно на char, так что это должен быть тот же результат.

2.) Вы перезаписать изображение на каждом шаге итерации, это означает, что в фильтре Pattern:

------- 
|1|2|3| 
------- 
|4|5|6| 
------- 
|7|8|9| 
------- 

значения 1,2,3 и 4 уже сглажены, и 5 вычисляется из 5 unsmoothend и 4 сглаженных пикселей. Я предлагаю создать новое пустое изображение и сохранить результат (темп) в новом изображении при чтении с оригинала - т. Е. Не пытаться обрабатывать «на месте» с выходным изображением так же, как и вход образ.

+1

. Хороший ответ. Надеюсь, вы не против моих маленьких изменений/разъяснений. –

+0

Ух, так много орфографических ошибок :-D сейчас лучше. Спасибо – Jounathaen

+0

Я действительно не понимаю, почему появились отрицательные значения. Если бы я сделал их положительными или превратил их в ноль, изображения полностью ошиблись. – user2752471

0

Я использовал фильтр размытия 3x3 в формате изображения BGR.

Важные замечания:

  • При фильтрации и изображения в BRG (или RGB) формате, необходимо фильтровать каждый цветовой канал отдельно.
  • При выполнении фильтра (с использованием свертки) важно использовать , а не, чтобы использовать вычисление «на месте» - избегать источника, перезаписываемого целевыми элементами.
  • Умножьте на (1/9) для эффективного, чем деление на 9.
  • Добавление 0,5 до цельного литья может использоваться для округления положительного значения.
  • Реализация фиксированной точки (1/9) масштабирования, выполняемая расширением, масштабированием и сдвигом. Пример: avg = (сумма * шкала + округление) >> 15; [Когда масштаб = (1/9) * 2^15].

Предполагаемый порядок пикселей в памяти: b, g, r, b, g, r ... (b находится в байте 0).

При фильтрации изображения в формате BRG (или RGB) вы должны фильтровать каждый цветовой канал отдельно!

Иллюстрация цветоделения:

bgrbgrbgr
bgrbgrbgr
bgrbgrbgr

Отдельные на:

БББ GGG ррр
БББ GGG ррр
БББ GGG ррр

размывания Ядро фильтра:
1/9, 1/9, 1/9
1/9, 1/9, 1/9
1/9, 1/9, 1/9
Как уже упоминалось, фильтр должен быть применен к Синий, зеленый и красный.

Вы можете суммировать каждый 3x3 пикселя и делить результат на 9 (или лучше умножить на 1/9, как и вы).
Я использовал реализацию фиксированной точки умножить на 1/9 вместо преобразования в float.
Реализация фиксированной точки используется для повышения производительности (не так важно здесь, поскольку код не оптимизирован).
я привычный, чтобы продемонстрировать технику ...

Я использовал некоторые кода дублирования, чтобы упор цветоделение RGB.
Я также обрабатывал границы изображения (используйте симметричное зеркалирование).

Вот мой код:

//Filter image I with filter kernel: 
// 1/9 1/9 1/9 
// 1/9 1/9 1/9 
// 1/9 1/9 1/9 
//I - Input image in pixel ordered BGR format 
//image_width - Number of columns of I 
//image_height - Number of rows of I 
//J - Destination "smoothed" image in BGR format. 

//I and J is pixel ordered BGR color format (size in bytes is image_width*image_height*3): 
//BRGBRGBRGBRGBR 
//BRGBRGBRGBRGBR 
//BRGBRGBRGBRGBR 
//BRGBRGBRGBRGBR 
// 
//Limitations: 
//I and J must be two separate arrays (in place computation is not supported). 
void BgrSmoothing(const unsigned char I[], 
        int image_width, 
        int image_height, 
        unsigned char J[]) 
{ 
    const int scale = (int)((1.0/9.0)*(1 << 15) + 0.5); //1/9 expanded by 2^15 (add 0.5 for rounding). 
    const int rounding_ofs = (1 << 14);     //0.5 expanded by 2^14 

    int x, y; 
    const unsigned char *I0; //Points beginning of row y-1 (in source image I). 
    const unsigned char *I1; //Points beginning of row y (in source image I). 
    const unsigned char *I2; //Points beginning of row y+1 (in source image I). 
    int x0, x1, x2;    //x0 - pixel to the left of x1, x1 - center, x2 - pixel to the right of x1 

    unsigned char *J1;   //Points beginning of row y (in destination image J). 

    //3x3 source blue pixels, 3x3 source green pixels, 3x3 source red pixels. 
    unsigned char b00, b01, b02, g00, g01, g02, r00, r01, r02; 
    unsigned char b10, b11, b12, g10, g11, g12, r10, r11, r12; 
    unsigned char b20, b21, b22, g20, g21, g22, r20, r21, r22; 

    unsigned char b, g, r; //Destination blue, green and red pixels. 

    for (y = 0; y < image_height; y++) 
    { 
     if (y == 0) 
      I0 = I;       //Handle first row: use row 0 instead of row -1 (row -1 exceeds image bounds). 
     else 
      I0 = &I[(y-1)*image_width*3]; //Pointer to beginning of source row above row y in image I. 

     I1 = &I[y*image_width*3];   //Pointer to beginning of source row y in image I. 

     if (y == image_height-1) 
      I2 = &I[y*image_width*3];  //Handle last row: use row image_height-1 instead of row image_height (row image_height exceeds image bounds). 
     else 
      I2 = &I[(y+1)*image_width*3]; //Pointer to beginning of source row below row y in image I. 

     J1 = &J[y*image_width*3];   //Pointer to beginning of destination row in image J. 

     //Handle first pixel: 
     for (x = 0; x < image_width; x++) 
     { 
      //Multiply x by 3, to convert pixel index to byte index (in BGR forst each pixel is 3 bytes). 
      x0 = (x == 0) ? (0) : (x-1)*3;        //Handle x0 coordinate of first pixel in the row. 
      x1 = x*3; 
      x2 = (x == image_width-1) ? (image_width-1)*3 : (x+1)*3; //Handle x2 coordinate of last pixel in the row. 

      //Load 3x3 blue pixels: 
      b00 = I0[x0]; b01 = I0[x1], b02 = I0[x2]; 
      b10 = I1[x0]; b11 = I1[x1], b12 = I1[x2]; 
      b20 = I2[x0]; b21 = I2[x1], b22 = I2[x2]; 

      //Load 3x3 green pixels: 
      g00 = I0[x0+1]; g01 = I0[x1+1], g02 = I0[x2+1]; 
      g10 = I1[x0+1]; g11 = I1[x1+1], g12 = I1[x2+1]; 
      g20 = I2[x0+1]; g21 = I2[x1+1], g22 = I2[x2+1]; 

      //Load 3x3 red pixels: 
      r00 = I0[x0+2]; r01 = I0[x1+2], r02 = I0[x2+2]; 
      r10 = I1[x0+2]; r11 = I1[x1+2], r12 = I1[x2+2]; 
      r20 = I2[x0+2]; r21 = I2[x1+2], r22 = I2[x2+2]; 

      //Sum all 9 blue elements, all 9 green elements and all 9 red elements (convert to int to avoid overflow). 
      int sum_b = (int)b00+(int)b01+(int)b02+(int)b10+(int)b11+(int)b12+(int)b20+(int)b21+(int)b22; 
      int sum_g = (int)g00+(int)g01+(int)g02+(int)g10+(int)g11+(int)g12+(int)g20+(int)g21+(int)g22; 
      int sum_r = (int)r00+(int)r01+(int)r02+(int)r10+(int)r11+(int)r12+(int)r20+(int)r21+(int)r22; 

      //b = round(sum_b*(1/9)). 
      //Because b i positive round(b) = floor(b+0.5). 
      //Use following computation instead: b = floor((sum_b*(1.0/9.0)*2^15 + 2^14)/2^15) 
      b = (unsigned char)((sum_b*scale + rounding_ofs) >> 15); //Destination blue pixel. 
      g = (unsigned char)((sum_g*scale + rounding_ofs) >> 15); //Destination green pixel. 
      r = (unsigned char)((sum_r*scale + rounding_ofs) >> 15); //Destination red pixel. 

      //Store b,g,r elements to destination row J1. 
      J1[x1]  = b; 
      J1[x1+1] = g; 
      J1[x1+2] = r; 
     } 
    } 
} 

Входное изображение:
enter image description here

Вывод изображения:
enter image description here

+0

Я не понимаю добавление 0.5 и вещи с фиксированной точкой. Во всяком случае, как я уже упоминал выше, я все время делаю с символами, и это ничего не меняет. Различные материалы массива, которые я сделал после того, как Jounathaen комментирует и обрабатывают каждый цветной канал отдельно, я уже делал. – user2752471

+0

@ user2752471 Когда я увидел ваши выходные изображения, я думал, что у вас есть ошибка, разделяющая цвета. Я также подчеркнул это для других, читающих мой ответ. – Rotem

+0

@ user2752471 Добавление 0.5 четырех округлений прост для понимания: round (x) = floor (x + 0.5). Поскольку целочисленный листинг аналогичен положению для положительных значений, вы можете использовать: round (x) = (без знака) (x + 0,5). Для неподвижной точки - разложите ее на 2^15, и вы получите: (x + 0,5) = (x + 0,5) * (2^15/2^15) = (x * 2^15 + 0,5 * 2^15) * 2^15 = (x * 2^15 + 2^14)/2^15 (поэтому для округления нужно добавить 2^14). – Rotem