Гауссовский фильтр C++

100
21 апреля 2022, 10:10

Написал программу, которая применяет фильтр Гаусса на зашумленное одноканальное изображение. Проблема в том, что на выходе оно получается несколько темнее, чем ожидалось. Не подскажете, в чем может быть проблема?

double** getGaussian(int height, int width, double sigma) {
    double** filter = new double * [width];
    for (size_t i = 0; i < width; ++i) {
        filter[i] = new double[height];
    }
    double sum = 0;
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            filter[i][j] = exp(-(i * i + j * j) / (2 * sigma * sigma)) / (2 * 3.14 * sigma * sigma);
            sum += filter[i][j];
        }
    }
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            filter[i][j] /= sum;
        }
    }
    return filter;
}
Image GaussianBlur(Image img, double** filter, int c, int r) {
    Image newimg;
    newimg.cols = img.cols - c + 1;;
    newimg.rows = img.rows - r + 1;;
    newimg.data = new unsigned char * [newimg.rows];
    for (size_t i = 0; i < newimg.rows; ++i) {
        newimg.data[i] = new uchar[newimg.cols];
    }
    for (size_t y = 0; y < newimg.rows; y++) {
        for (size_t x = 0; x < newimg.cols; x++) {
            int sum = 0;
            for (int h = y; h < y + r; h++) {
                for (int w = x; w < x + c; w++) {
                    sum += filter[h - y][w - x] * img.data[h][w];
                }
            }
            newimg.data[y][x] = sum;
        }
    }
    for (size_t y = 0; y < c; ++y) {
        delete[] filter[y];
    }
    delete[] filter;
    return newimg;
}
double FindSigma(const Image& img) {
    double mean = 0;
    double sum = 0;
    double count = 0;
    for (size_t y = 0; y < img.rows; y++) {
        for (size_t x = 0; x < img.cols; x++) {
            count += 1;
            sum += img.data[y][x];
        }
    }
    mean = sum / count;
    double sigma = 0;
    for (size_t y = 0; y < img.rows; y++) {
        for (size_t x = 0; x < img.cols; x++) {
            sigma += (img.data[y][x] - mean) * (img.data[y][x] - mean);
        }
    }
    sigma /= count;
    return sigma;
}

Функция getGaussian создает массив заданного размера (height, width), в котором хранятся значения фильтра Гаусса. Формула для расчета значений такая -g(x, y) = 1 / (2 * pi * sigma ^ 2) * exp(-(x^2 + y ^2)/(2*sigma^2)) (из Википедии), где сигма - стандартное отклонение, а g(x,y) - значения в фильтре. Тип double выбран для того, чтобы при умножении на unsigned char (тип данных во входном изображении) не получались нули. Image - это такая структура:

struct Image {
    unsigned char** data = nullptr;
    size_t rows = 0;
    size_t cols = 0;
};

Вызов функции происходит так

double sigma = FindSigma(image);
double** filter = getGaussian(5, 5, sigma);
Image gauss = GaussianBlur(image, filter, 5, 5);

Помогите найти ошибку, пожалуйста.

Answer 1

Для начала - пространственный параметр сигма ядра Гаусса не связан с вариацией элементов массива. Он выбирается таким образом, чтобы на краях ядра коэффициенты были малЫ. Для ядра 5x5 радиус 2, и сигма=1 - хороший выбор. Либо наоборот - для заданной сигмы r=2*sigma.

Далее - ядро фильтра Гаусса - центрированное нечётного размера, наибольший коэффициент находится в центре. Таким образом, имея r=2, получаем size=2*r+1 = 5. Коэффициент перед экспонентой не нужен, т.к. далее идёт нормировка. А отклонения считаются от центра.

 getGaussian(int r, double sigma)   //r=2, sigma=1  для size=5x5
 ...
 double sum = 0;
    for (int i = 0; i <= 2*r; i++) {
        for (int j = 0; j <= 2*r; j++) {
            filter[i][j] = exp(-((i-r)*(i-r)+(j-r)*(j-r))/(2*sigma*sigma));
            sum += filter[i][j];
        }
    }
...

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

for (size_t y = r; y < newimg.rows - r; y++) {
        for (size_t x = r; x < newimg.cols - r; x++) {
            int sum = 0;
            for (int h = 0; h < r; h++) {
                for (int w = 0; w < r; w++) {
                    sum += filter[h][w] * img.data[y + h - r][x + w  - r];
                }
            }
            newimg.data[y][x] = sum;
        }
    }
READ ALSO
Sql запрос и обработка на php

Sql запрос и обработка на php

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

191
После require_once() откуда-то возникает единичка

После require_once() откуда-то возникает единичка

Очень странная ситуация, глупая, но не решаемая уже много часов

100
Отображение html в окне ввода

Отображение html в окне ввода

Имеем файл indexphp В нем такой код

113
Получить абсолютный путь в Slim 4 + Twig

Получить абсолютный путь в Slim 4 + Twig

Всем привет! Решил переехать на Slim Framework 4Возникла проблема

110