Выход сглаживания по Гауссу смещен

Я пытаюсь выполнить сглаживание по Гауссу на этом изображении без использования какой-либо функции opencv (кроме отображения изображения).

входное изображение

Однако результат, который я получил после свертки изображения с гауссовым ядром, выглядит следующим образом:

выходное изображение

Выходное изображение кажется смещенным и выглядит очень странно. Есть идеи, что происходит?

Создать ядро ​​Гаусса:

double gaussian(int x, int y,double sigma){
    return (1/(2*M_PI*pow(sigma,2)))*exp(-1*(pow(x,2)+pow(y,2))/(2*pow(sigma,2)));
}



double generateFilter(vector<vector<double>> & kernel,int width,double sigma){
    int value = 0;
    double total =0;

    if(width%2 == 1){
        value = (width-1)/2;
    }else{
        value = width/2;
    }

    double smallest = gaussian(-1*value,-1*value,sigma);

    for(int i = -1*value; i<=value; i++){
        vector<double> temp;
        for(int k = -1*value; k<=value; k++){
            int gVal = round(gaussian(i,k,sigma)/smallest);
            temp.push_back(gVal);
            total += gVal;

        }
        kernel.push_back(temp);

    }

    cout<<total<<endl;

    return total;


}

Свертка:

vector<vector<unsigned int>>  convolution(vector<vector<unsigned int>> src,  vector<vector<double>> kernel,double total){
    int kCenterX = floor(kernel.size() / 2); //center of kernel
    int kCenterY = kCenterX; //center of kernel
    int kRows = kernel.size(); //height of kernel
    int kCols = kRows; //width of kernel
    int imgRows = src.size(); //height of input image
    int imgCols = src[0].size(); //width of input image
    vector<vector<unsigned int>> dst = vector<vector<unsigned int>> (imgRows, vector<unsigned int>(imgCols ,0));


    for ( size_t row = 0; row < imgRows; row++ ) { 
        for ( size_t col = 0; col < imgCols; col++ ) {
        float accumulation = 0;
        float weightsum = 0; 
        for ( int i = -1*kCenterX; i <= 1*kCenterX; i++ ) {
            for ( int j = -1*kCenterY; j <= 1*kCenterY; j++ ) {
                int k = 0;

                if((row+i)>=0 && (row+i)<imgRows && (col+j)>=0 && (col+j)<imgCols){
                    k = src[row+i][col+j];
                    weightsum += kernel[kCenterX+i][kCenterY+j];
                }

                accumulation += k * kernel[kCenterX +i][kCenterY+j];


            }
        } 
        dst[row][col] = round(accumulation/weightsum);
        }

    }

    return dst;
}

Спасибо.


person jetjetboi    schedule 01.03.2020    source источник
comment
Где код??? Как мы должны вам помочь, если вы не предоставите свой код??   -  person stateMachine    schedule 01.03.2020
comment
добавил код, спасибо   -  person jetjetboi    schedule 01.03.2020


Ответы (1)


Функция свертки в основном верна, поэтому проблема связана с форматом ввода и вывода.

  • Убедитесь, что вы читаете изображение в оттенках серого (а не в RGB):

    cv::Mat I = cv::imread("img.png", cv::IMREAD_GRAYSCALE);
    
  • Вы передаете аргумент vector<vector<unsigned int>> в convolution.
    Я не могу сказать, является ли это частью проблемы или нет, но рекомендуется передавать аргумент типа cv::Mat (и возвращать cv::Mat):

    cv::Mat convolution(cv::Mat src, vector<vector<double>> kernel, double total)
    

    Я предполагаю, что вы можете преобразовать ввод в vector<vector<unsigned int>> и обратно, но это не обязательно.

Вот пример рабочего кода:

#include <vector>
#include <iostream>

#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"

using namespace std;


double gaussian(int x, int y, double sigma) {
    return (1 / (2 * 3.141592653589793*pow(sigma, 2)))*exp(-1 * (pow(x, 2) + pow(y, 2)) / (2 * pow(sigma, 2)));
}


double generateFilter(vector<vector<double>> & kernel, int width, double sigma)
{
    int value = 0;
    double total = 0;

    if (width % 2 == 1) {
        value = (width - 1) / 2;
    }
    else {
        value = width / 2;
    }

    double smallest = gaussian(-1 * value, -1 * value, sigma);

    for (int i = -1 * value; i <= value; i++) {
        vector<double> temp;
        for (int k = -1 * value; k <= value; k++) {
            int gVal = round(gaussian(i, k, sigma) / smallest);
            temp.push_back(gVal);
            total += gVal;
        }
        kernel.push_back(temp);    
    }

    cout << total << endl;

    return total;
}


//vector<vector<unsigned int>>  convolution(vector<vector<unsigned int>> src,  vector<vector<double>> kernel, double total) {
cv::Mat convolution(cv::Mat src, vector<vector<double>> kernel, double total) {
    int kCenterX = floor(kernel.size() / 2); //center of kernel
    int kCenterY = kCenterX; //center of kernel
    int kRows = kernel.size(); //height of kernel
    int kCols = kRows; //width of kernel
    int imgRows = src.rows;//src.size(); //height of input image
    int imgCols = src.cols;//src[0].size(); //width of input image
    //vector<vector<unsigned int>> dst = vector<vector<unsigned int>> (imgRows, vector<unsigned int>(imgCols ,0));
    cv::Mat dst = cv::Mat::zeros(src.size(), CV_8UC1);  //Create destination matrix, and fill with zeros (dst is Grayscale image with byte per pixel).

    for (size_t row = 0; row < imgRows; row++) {
        for (size_t col = 0; col < imgCols; col++) {
            double accumulation = 0;
            double weightsum = 0;
            for (int i = -1 * kCenterX; i <= 1 * kCenterX; i++) {
                for (int j = -1 * kCenterY; j <= 1 * kCenterY; j++) {
                    int k = 0;

                    if ((row + i) >= 0 && (row + i) < imgRows && (col + j) >= 0 && (col + j) < imgCols) {
                        //k = src[row+i][col+j];
                        k = (int)src.at<uchar>(row + i, col + j);   //Read pixel from row [row + i] and column [col + j]
                        weightsum += kernel[kCenterX + i][kCenterY + j];
                    }

                    accumulation += (double)k * kernel[kCenterX + i][kCenterY + j];
                }
            }
            //dst[row][col] = round(accumulation/weightsum);
            dst.at<uchar>(row, col) = (uchar)round(accumulation / weightsum);   //Write pixel from to row [row] and column [col]

            //dst.at<uchar>(row, col) = src.at<uchar>(row, col);
        }

    }

    return dst;
}


int main()
{
    vector<vector<double>> kernel;
    double total = generateFilter(kernel, 11, 3.0);

    //Read input image as Grayscale (one byte per pixel).
    cv::Mat I = cv::imread("img.png", cv::IMREAD_GRAYSCALE);

    cv::Mat J = convolution(I, kernel, total);

    //Display input and output
    cv::imshow("I", I);
    cv::imshow("J", J);
    cv::waitKey(0);
    cv::destroyAllWindows();

    return 0;
}

Результат:
введите здесь описание изображения

person Rotem    schedule 01.03.2020
comment
Спасибо за ваш вклад. Любая идея, почему vector‹vector‹unsigned int›› не работает? Данные внутри вектора должны быть такими же, как cv::Mat, верно? - person jetjetboi; 01.03.2020
comment
Это должно быть vector<vector<unsigned char>>, и вам нужен цикл, который повторяет строки и строит вектор векторов (cv:Mat не хранится как вектор или векторы внутри). Вот пример копирования данных. Осуществление преобразования из Mat в vector из vectors без копирования данных очень запутанно. - person Rotem; 01.03.2020