使用 C 对图像和蒙版进行二维离散卷积

2D Discrete Convolution of Image and Mask using C

我正在尝试为灰度 bmp 图像制作卷积算法。下面的代码来自 Udemy 上的图像处理课程,但是对使用的变量和公式的解释有点简短。问题出在二维离散卷积部分,我无法理解这里实现的公式

struct Mask{
int Rows;
int Cols;
unsigned char *Data;
};

int main()
{
    int imgWidth, imgHeight, imgBitDepth;
    unsigned char imgHeader[BMP_HEADER_SIZE];
    unsigned char imgColorTable[BMP_COLOR_TABLE_SIZE];
    unsigned char imgBuffer[CUSTOM_IMG_SIZE];
    unsigned char imgBuffer2[CUSTOM_IMG_SIZE];

    const char imgName[] = "images/cameraman.bmp";
    const char newImgName[] = "images/cameraman_new.bmp";

    struct Mask lpMask;
    signed char *tmp;
    int i;

    lpMask.Cols = lpMask.Rows = 5;
    lpMask.Data = (unsigned char *)malloc(25);

    /* -1 -1 -1 -1 -1
       -1 -1 -1 -1 -1
       -1 -1 24 -1 -1
       -1 -1 -1 -1 -1
       -1 -1 -1 -1 -1*/

    //set all mask values to -1
    tmp = (signed char *)lpMask.Data;
    for (i = 0; i < 25; ++i)
    {
        *tmp = -1;
        ++tmp;
    }
    //set middle value to 24
    tmp = (signed char *)lpMask.Data + 13;
    *tmp = 24;

    imageReader(imgName, &imgHeight, &imgWidth, &imgBitDepth, imgHeader, imgColorTable, imgBuffer);
    Convolve(imgHeight, imgWidth, &lpMask, imgBuffer, imgBuffer2);
    imageWriter(newImgName, imgHeader, imgColorTable, imgBuffer2, imgBitDepth);

    printf("Success!\n");

    return 0;
}

//2D Discrete Convolution
void Convolve(int imgRows, int imgCols, struct Mask *myMask, unsigned char *input_buf, unsigned char *output_buf)
{
    long i, j, m, n, idx, jdx;
    int ms, im, val;
    unsigned char *tmp;

    //outer summation loop - image
    for (i = 0; i < imgRows; ++i)
        //inner summation loop - image
        for (j = 0; j < imgCols; ++j)
        {
            val = 0;

            //outer summation loop - mask
            for (m = 0; m < myMask->Rows; ++m)
                //inner summation loop - mask
                for (n = 0; n < myMask->Cols; ++n)
                {
                    


                    //Issue in understanding below part
                    ms = (signed char)*(myMask->Data + m * myMask->Rows + n);
                    // index of input img, used for checking boundary
                    idx = i - m;
                    jdx = j - n;
                    if (idx >= 0 && jdx >= 0)  //ignore input samples which are out of bound
                    im = *(input_buf + idx * imgRows + jdx);        
                    val += ms * im;
                }
                //truncate values to remain inside 0to255 range
            if (val > 255) val = 255;
            if (val < 0)   val = 0;
            tmp = output_buf + i * imgRows + j;
            *tmp = (unsigned char)val;
        }
}

这里有3行,使用的公式相似,最难理解其实现,如果可能请帮助理解这些代码逻辑或它们到底在做什么:

ms = (signed char)*(myMask->Data + m * myMask->Rows + n);
im = *(input_buf + idx * imgRows + jdx);
tmp = output_buf + i * imgRows + j;

对于 formula/pseudocode 使用,检查以下网站上的卷积部分:- https://en.wikipedia.org/wiki/Kernel_(image_processing)

g(x,y) = ∑k= -n2 到 n2 ∑j= -m2 到 m2 h(j,k) * f(x-j, y-k) , 其中 m2 = 掩码宽度的一半 & n2 = 掩码高度的一半

您询问的表达式只是计算以二维(行、列)索引的特定像素的位置,存储在平面内存缓冲区中。

例如,ms = (signed char)*(myMask->Data + m * myMask->Rows + n);从掩码图像数据缓冲区本身开始,myMask->Data,它是一个指针。第一行数据首先显示,然后是第二行。所以要访问m行n列的像素,首先要跳过m行数据,也就是行*m的大小。然后你必须跳过行内的 n 个像素。一旦像素的位置被计算出来,它就会被取消引用 *.

我对这个示例代码的唯一抱怨是名称 myMask->Rows。在这种情况下,m 表示行索引,为了计算偏移量,它乘以行的大小,应该 是图像中的列数,而不是列数的行。因此该参考应改为 myMask->Cols.