2017-03-02 87 views
0

我一直在嘗試使用2D卷積來處理圖像處理的項目。因爲我只需要通過像素像素卷積我決定從維基百科使用下面的代碼(我知道這是醜陋的,而不是優化。)用數學公式:C中邊緣檢測的卷積

output[1][1] = b[0][0]*mask_0[2][2] + b[0][1]*mask_0[2][1] + b[0][2]*mask_0[2][0] 
       + b[1][0]*mask_0[1][2] + b[1][1]*mask_0[1][1] + b[1][2]*mask_0[1][0] 
       + b[2][0]*mask_0[0][2] + b[2][1]*mask_0[0][1] + b[2][2]*mask_0[0][0] 

我使用基爾希邊緣檢測。 後不幸與卷積只是一個掩蓋產生的圖像是:

After Convolution

Before Convolution

+0

賦值的右側不依賴於'i'和'z',所以似乎會在每個輸出像素處放置相同的值。 – interjay

+0

對不起。輸出數組被循環並且緩衝區部分改變。爲了簡潔起見,我沒有把它們放在代碼部分。我應該發佈整個代碼嗎? –

+0

你使用了什麼掩碼值?就像[維基百科文章]中提到的那樣(https://en.wikipedia.org/wiki/Multidimensional_discrete_convolution#Motivation_.26_Applications)? – Groo

回答

1

我做了一個最小的完整的例子卷積與我跑,你描述的算法。

我這樣做是直接的方式。這非常適合學習,但不適合串行使用(缺少任何優化以保持代碼清晰可讀)。

#include <assert.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

typedef unsigned char uint8; 
typedef unsigned int uint; 

typedef struct { 
    uint w, h; 
    uint8 *data; 
} Image; 

uint newImage(Image *pImg, uint w, uint h) 
{ 
    uint size = w * h * 3; 
    if (!pImg) return 0; 
    assert(!pImg->data); 
    pImg->data = malloc(size); 
    pImg->w = pImg->data ? w : 0; pImg->h = pImg->data ? h : 0; 
    if (!pImg->data) { 
    fprintf(stderr, 
     "Allocation of %u bytes for image data failed!\n", size); 
    return 0; 
    } 
    return size; 
} 

void fillImage(Image *pImg, uint8 r, uint8 g, uint8 b) 
{ 
    if (!pImg || !pImg->data) return; 
    { uint size = pImg->w * pImg->h * 3, i; 
    for (i = 0; i < size; i += 3) { 
     pImg->data[i] = r; pImg->data[i + 1] = g; pImg->data[i + 2] = b; 
    } 
    } 
} 

void freeImage(Image *pImg) 
{ 
    if (!pImg) return; 
    free(pImg->data); 
    pImg->data = 0; 
} 

int readPPM(FILE *f, Image *pImg) 
{ 
    char buffer[32] = ""; uint w = 0, h = 0, t = 0, size = 0, i = 0; 
    if (!pImg) return 0; 
    assert(!pImg->data); 
    /* parse header */ 
    if ((i = 1, !fgets(buffer, sizeof buffer, f)) 
    || (i = 2, strcmp(buffer, "P6\n") != 0) 
    || (i = 3, fscanf(f, "%u %u %u", &w, &h, &t) != 3) 
    || (i = 4, t != 255)) { 
    fprintf(stderr, "Not a PPM image! (%u)\n", i); 
    return -1; 
    } 
    /* allocate appropriate memory */ 
    if (!(size = newImage(pImg, w, h))) return -1; 
    /* read data */ 
    if (fread(pImg->data, 1, size, f) != size) { 
    fprintf(stderr, "Not enough data in PPM image!\n"); 
    return -1; 
    } 
    /* done */ 
    return 0; 
} 

void writePPM(FILE *f, Image *pImg) 
{ 
    if (!pImg || !pImg->data) return; 
    fprintf(f, "P6\n%u %u 255\n", pImg->w, pImg->h); 
    { uint size = pImg->w * pImg->h * 3, i; 
    for (i = 0; i < size; i += 3) { 
     fprintf(f, "%c%c%c", 
     pImg->data[i], pImg->data[i + 1], pImg->data[i + 2]); 
    } 
    } 
} 

#define GET_PIXEL(P_IMG, ROW, COL, C) \ 
    ((P_IMG)->data[((ROW) * (P_IMG)->w + (COL)) * 3 + (C)]) 

void convolute(
    Image *pImg, uint dim, int *mat, 
    Image *pImgOut) 
{ 
    if (!pImg || !pImg->data) return; 
    assert(dim & 1); /* dim Mat must be odd */ 
    { int offs = -(dim/2); 
    unsigned i, j; 
    for (i = 0; i < pImg->h; ++i) { 
     for (j = 0; j < pImg->w; ++j) { 
     unsigned iM, jM; 
     uint8 *pixelOut = pImgOut->data + (i * pImg->w + j) * 3; 
     int r = 0, g = 0, b = 0; 
     for (iM = 0; iM < dim; ++iM) { 
      for (jM = 0; jM < dim; ++jM) { 
      int mIJ = mat[iM * dim + jM]; 
      r += mIJ 
       * (int)GET_PIXEL(pImg, 
       (pImg->h + i + offs + iM) % pImg->h, 
       (pImg->w + j + offs + jM) % pImg->w, 
       0); 
      g += mIJ 
       * (int)GET_PIXEL(pImg, 
       (pImg->h + i + offs + iM) % pImg->h, 
       (pImg->w + j + offs + jM) % pImg->w, 
       1); 
      b += mIJ 
       * (int)GET_PIXEL(pImg, 
       (pImg->h + i + offs + iM) % pImg->h, 
       (pImg->w + j + offs + jM) % pImg->w, 
       2); 
      } 
     } 
#if 1 /* colored output */ 
     pixelOut[0] = (uint8)abs(r); 
     pixelOut[1] = (uint8)abs(g); 
     pixelOut[2] = (uint8)abs(b); 
#else /* gray level output */ 
     pixelOut[0] = pixelOut[1] = pixelOut[2] 
      = abs(r) + abs(g) + abs(b)/3; 
#endif /* 1 */ 
     } 
    } 
    } 
} 

int main(int argc, char **argv) 
{ 
    enum { Dim = 3 }; 
#if 0 
    int mat[Dim * Dim] = { 
    0, -1, 0, 
    -1, 4, -1, 
    0, -1, 0 
    }; 
#endif 
    int mat[Dim * Dim] = { 
    -1, -1, -1, 
    -1, 8, -1, 
    -1, -1, -1 
    }; 

    FILE *f = 0; 
    const char *file, *outFile; 
    /* read command line arguments */ 
    if (argc <= 2) { 
    fprintf(stderr, "Missing command line arguments!\n"); 
    printf("Usage:\n" 
     " $ %s <IN_FILE> <OUT_FILE>\n", 
     argv[0]); 
    return -1; 
    } 
    file = argv[1]; outFile = argv[2]; 
    /* read PPM image */ 
    if (!(f = fopen(file, "rb"))) { 
    fprintf(stderr, "Cannot open input file '%s'!\n", file); 
    return -1; 
    } 
    Image img = { 0, 0, NULL }; 
    if (readPPM(f, &img)) return -1; 
    fclose(f); f = 0; 
    /* make output image */ 
    Image imgOut = { 0, 0, NULL }; 
    newImage(&imgOut, img.w, img.h); 
    /* convolute image */ 
    convolute(&img, Dim, mat, &imgOut); 
    /* write PPM image */ 
    if (!(f = fopen(outFile, "wb"))) { 
    fprintf(stderr, "Cannot create output file '%s'!\n", outFile); 
    return -1; 
    } 
    writePPM(f, &imgOut); 
    fclose(f); 
    /* done */ 
    return 0; 
} 

我編譯和在Windows 10 VS2013以及海灣合作委員會在Cygwin中進行了測試:

$ gcc -o edge-detect edge-detect.c 

$ ./edge-detect.exe fluffyCat.64x64.ppm edge-detect-out.ppm 

$ 

fluffyCat.64x64.ppm看起來是這樣的: fluffyCat.64x64.png

邊緣detect- out.ppm看起來是這樣的: edge-detect-out.png

一些注意事項:

我用古老的X11 PPM格式,因爲

  1. 它可以讀取和用最少的代碼編寫,並,因此,對於這些樣本最佳擬合。
  2. 它在GIMP中受支持。因此,創建和查看很容易。

該代碼的靈感來源於Creating, Compiling, and Viewing ppm Images,可能無法處理任何PPM風味。

注意!當GIMP保存PPM時,它包含示例代碼中的閱讀器無法閱讀的註釋。我只是用文本編輯器刪除了這個評論。 保存GIMP設置:原始數據

這種圖像處理算法常見的危險是處理邊界像素(其中矩陣可能應用於圖像外部的相鄰非現有像素)。我簡單地通過環繞圖像來解決它(使用resp。index模寬/圖像高度)。

在卷積中,我用abs()來保持輸出的正範圍。不幸的是,我不能說這是否完全正確。 (這是22年前我關心大學圖像處理的心得。)

0

您在第二行的末尾出現了拼寫錯誤:

output[1][1] = b[0][0]*mask_0[2][2] + b[0][1]*mask_0[2][1] + b[0][2]*mask_0[2][0] 
       + b[1][0]*mask_0[1][2] + b[1][1]*mask_0[1][1] + b[1][2]*mask_0[2][1] // Here, it should be [1][0] 
       + b[2][0]*mask_0[0][2] + b[2][1]*mask_0[0][1] + b[2][2]*mask_0[0][0] 

而且,你不必擔心數學公式,其中索引「以相反的順序去」。爲了簡便起見,只是做

output[1][1] = b[0][0]*mask_0[0][0] + b[0][1]*mask_0[0][1] + b[0][2]*mask_0[0][2] 
       + b[1][0]*mask_0[1][0] + ... 

當然,相應地更新你的面具,但如果是對稱的,你甚至不必更新它。

+0

Ooops沒有看到。不幸的是,在我的代碼中,我說得對。我也嘗試過你的解決方案,因爲我使用了7個相互對稱的蒙版,但結果仍然保持不變。 –

+0

那麼,現在你知道人們爲什麼要求[MVCE](http://stackoverflow.com/help/mcve),它可以防止這些問題=) – Fezvez

+0

對不起,先發布。我會看看。 –