【问题标题】:Gaussian Blur, Mean Filter, Convolution高斯模糊、均值滤波器、卷积
【发布时间】:2013-10-25 03:44:36
【问题描述】:

我想实现一个卷积函数以在均值滤波器和高斯滤波器中使用,我还需要实现这两个滤波器以应用于 pgm 文件。 我有

typedef struct _PGM{
int row;
int col;
int max_value;
int **matrix;
}PGM;

结构和

int convolution(int ** kernel,int ksize, PGM * image, PGM * output){

   int i, j, x, y;
   int sum;
   int data;
   int scale =ksize*ksize;
   int coeff;

 for (x=ksize/2; x<image->row-ksize/2;++x) {
  for (y=ksize/2; y<image->col-ksize/2; ++y){
     sum = 0;
    for (i=-ksize/2; i<=ksize/2; ++i){
      for (j=-ksize/2; j<=ksize/2; ++j){
        data = image->matrix[x +i][y +j];
        coeff = kernel[i+ksize/2][j+ksize/2];
        sum += data * coeff;
    }
  }
  output->matrix[x][y] = sum / scale; 
 }
}

return sum/scale;
}

卷积函数但我在卷积函数中得到错误(实际上它终止)所以我无法继续过滤 你能帮我实现吗?

谢谢。

【问题讨论】:

  • “我得到错误”不是一个有用的描述。确切的错误是什么?当您在调试器中运行此程序时,您发现了什么?
  • @OliCharlesworth 它说我的程序中出现了访问冲突,但是当我进行跟踪时,我无法理解我做错了什么。
  • 嗯,它在哪一行崩溃了?当时相关变量的值是多少?他们看起来神志清醒吗?
  • @OliCharlesworth 它在 coeff = kernel[i+ksize/2][j+ksize/2] 处崩溃;行,但大小似乎为 3,i 为 -1,j 为 -1,所以 coeff 应该等于 kernel[0][0] 我没有看到我遗漏的点。
  • 您确定所有涉及的指针都指向有效内存吗?

标签: c image-processing filtering gaussian convolution


【解决方案1】:

在您的卷积中,有两件事可能不会导致崩溃。第一个是样式:您使用x 迭代图像的行,我将其更多地描绘为y 位移,反之亦然。第二个是当您计算总和时,您不会在评估每个像素的内核(内部两个循环)之前重置变量sum = 0。相反,您在所有像素上累积 sum,可能最终导致整数溢出。虽然严格来说这是 UB,可能会导致崩溃,但这不是您面临的问题。

如果您愿意确认崩溃发生在第一个像素上(x = ksize/2y = ksize/2),那么由于崩溃发生在从内核读取的第一个系数处,我怀疑您可能通过了“错误的事情” " 作为kernel。如前所述,kernelint**。对于 3x3 的内核大小,这意味着要正确调用此函数,您必须在堆或堆栈上分配 int* 的数组,其中存储了 3 个指向 int 数组的指针,每个数组有 3 个系数。如果您改为传递int[3][3] 数组,卷积函数将尝试将数组中的第一个或两个int 解释为指向int 的指针,如果不是,并尝试取消引用它以拉入系数。这很可能会导致段错误。

我也不知道您为什么要退回累计金额。这不是卷积的“传统”输出,但我猜你对输出图像的平均亮度感兴趣,这是合法的;在这种情况下,您应该使用单独且更宽的整数累加器(longlong long),最后将其除以输出中的像素数。

您可能从 Internet 上找到了 PGM 数据结构,例如 here。请允许我放弃这个最佳实践建议。在我的领域(计算机视觉)中,选择的计算机视觉库 OpenCV 确实将矩阵表示为row 指向col 元素缓冲区的指针数组。相反,分配了一大块内存,在这种情况下,大小至少为image-&gt;row * image-&gt;col * sizeof(int),但通常image-&gt;row * image-&gt;step * sizeof(int),其中image-&gt;stepimage-&gt;col,四舍五入到下一个4 或16 的倍数。然后,只有一个保留单个指针,指向整个图像的基础的指针,但如果图像不连续,则必须保留一个额外的字段(步骤)。

因此我会修改你的代码:

/* Includes */
#include <stdlib.h>



/* Defines */
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))



/* Structure */

/**
 * Mat structure.
 * 
 * Stores the number of rows and columns in the matrix, the step size
 * (number of elements to jump from one row to the next; must be larger than or
 *  equal to the number of columns), and a pointer to the first element.
 */

typedef struct Mat{
    int  rows;
    int  cols;
    int  step;
    int* data;
} Mat;



/* Functions */

/**
 * Allocation. Allocates a matrix big enough to hold rows * cols elements.
 * 
 * If a custom step size is wanted, it can be given. Otherwise, an invalid one
 * can be given (such as 0 or -1), and the step size will be chosen
 * automatically.
 * 
 * If a pointer to existing data is provided, don't bother allocating fresh
 * memory. However, in that case, rows, cols and step must all be provided and
 * must be correct.
 * 
 * @param [in] rows         The number of rows of the new Mat.
 * @param [in] cols         The number of columns of the new Mat.
 * @param [in] step         The step size of the new Mat. For newly-allocated
 *                          images (existingData == NULL), can be <= 0, in
 *                          which case a default step size is chosen; For
 *                          pre-existing data (existingData != NULL), must be
 *                          provided.
 * @param [in] existingData A pointer to existing data. If NULL, a fresh buffer
 *                          is allocated; Otherwise the given data is used as
 *                          the base pointer.
 * @return An allocated Mat structure.
 */

Mat allocMat(int rows, int cols, int step, int* existingData){
    Mat M;

    M.rows = max(rows, 0);
    M.cols = max(cols, 0);
    M.step = max(step, M.cols);

    if(rows <= 0 || cols <= 0){
        M.data = 0;
    }else if(existingData == 0){
        M.data = malloc(M.rows * M.step * sizeof(*M.data));
    }else{
        M.data = existingData;
    }

    return M;
}

/**
 * Convolution. Convolves input by the given kernel (centered) and stores
 * to output. Does not handle boundaries (i.e., in locations near the border,
 * leaves output unchanged).
 * 
 * @param [in]  input  The input image.
 * @param [in]  kern   The kernel. Both width and height must be odd.
 * @param [out] output The output image.
 * @return Average brightness of output.
 * 
 * Note: None of the image buffers may overlap with each other.
 */

int convolution(const Mat* input, const Mat* kern, Mat* output){
    int i, j, x, y;
    int coeff, data;
    int sum;
    int avg;
    long long acc = 0;

    /* Short forms of the image dimensions */
    const int iw = input ->cols, ih = input ->rows, is = input ->step;
    const int kw = kern  ->cols, kh = kern  ->rows, ks = kern  ->step;
    const int ow = output->cols, oh = output->rows, os = output->step;

    /* Kernel half-sizes and number of elements */
    const int kw2   = kw/2,        kh2 = kh/2;
    const int kelem = kw*kh;

    /* Left, right, top and bottom limits */
    const int l = kw2,
              r = max(min(iw-kw2, ow-kw2), l),
              t = kh2,
              b = max(min(ih-kh2, oh-kh2), t);

    /* Total number of pixels */
    const int totalPixels = (r-l)*(b-t);

    /* Input, kernel and output base pointers */
    const int*  iPtr = input ->data;
    const int*  kPtr = kern  ->data + kw2 + ks*kh2;
    int*        oPtr = output->data;


    /* Iterate over pixels of image */
    for(y=t; y<b; y++){
        for(x=l; x<r; x++){
            sum = 0;

            /* Iterate over elements of kernel */
            for(i=-kh2; i<=kh2; i++){
                for(j=-kw2; j<=kw2; j++){
                    data   = iPtr[j + is*i + x];
                    coeff  = kPtr[j + ks*i    ];
                    sum   += data * coeff;
                }
            }

            /* Compute average. Add to accumulator and store as output. */
            avg      = sum / kelem;
            acc     += avg;
            oPtr[x]  = avg;
        }

        /* Bump pointers by one row step. */
        iPtr += is;
        oPtr += os;
    }

    /* Compute average brightness over entire output */
    if(totalPixels == 0){
        avg = 0;
    }else{
        avg = acc/totalPixels;
    }

    /* Return average brightness */
    return avg;
}


/**
 * Main
 */

int main(int argc, char* argv[]){
    /**
     * Coefficients of K. Binomial 3x3, separable. Unnormalized (weight = 16).
     * Step = 3.
     */

    int Kcoeff[3][3] = {{1, 2, 1}, {2, 4, 2}, {1, 2, 1}};

    Mat I = allocMat(1920, 1080, 0, 0);/* FullHD 1080p:  1920x1080 */
    Mat O = allocMat(1920, 1080, 0, 0);/* FullHD 1080p:  1920x1080 */
    Mat K = allocMat(   3,    3, 3, &Kcoeff[0][0]);

    /* Fill Mat I with something.... */

    /* Convolve with K... */
    int avg = convolution(&I, &K, &O);

    /* Do something with O... */

    /* Return */
    return 0;
}

参考:多年计算机视觉经验。

【讨论】:

  • 谢谢先生的帮助。我是计算机视觉的新手,实际上这是我的第一次尝试,所以我没有处理大图像,它是 500x500 灰度图像,用户需要输入内核及其大小,所以我更喜欢使用 int **,因为大小是不知道什么时候创建的。它应该返回总和/比例,但我编辑了它。我正在发送正确的内核,实际上我在调试时检查了它。我现在会尝试理解您的代码,但我想了解我的真正问题所在,因为我是一名学生,我需要从错误中吸取教训。
  • 除了缺少sum = 0 的问题和样式问题之外,您的代码对我来说实际上看起来还不错。这就是为什么我猜测它与您传递kernel 参数的方式有关。如果您传递的是int[3][3],它中断。如果您传递类似int k1[3] = {1, 2, 1}; int k2[3] = {2, 4, 2}; int k3[3] = {1, 2, 1}; int* K[3] = {k1, k2, k3}; convolution(K, 3, image, output); 之类的东西,它应该可以工作。
  • 代码优雅且易于阅读。你的经验肯定表明。我会偷这个=)
猜你喜欢
  • 2018-10-03
  • 2011-03-22
  • 1970-01-01
  • 1970-01-01
  • 2014-05-27
  • 2020-08-02
  • 1970-01-01
  • 1970-01-01
  • 2019-11-24
相关资源
最近更新 更多