C中边缘检测的卷积 [英] Convolution for Edge Detection in C

查看:117
本文介绍了C中边缘检测的卷积的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在尝试将2D Convolution用于有关图像处理的项目。因为我只需要逐个像素地进行卷积,所以我决定使用以下代码(我知道它很丑陋并且没有优化。)使用维基百科的数学公式:

 输出[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]

我正在使用Kirsch边缘检测。
不幸的是,在仅使用一个蒙版进行卷积后,生成的图像为:





edge-detect-out。 ppm看起来像这样:



一些注释:



我使用了古老的X11 PPM格式,因为


  1. 它可以用最少的代码读写,因此,最适合此类样品。

  2. GIMP支持。因此,创建和查看很容易。

代码的灵感来自于创建,编译和查看ppm图像,可能无法处理任何PPM风格。



注意!当GIMP保存PPM时,它包含一个注释,示例代码中的读者无法读取该注释。我只是用文本编辑器删除了这个评论。
用于保存的GIMP设置:原始数据



此类图像处理算法的一个常见危险是处理边框像素(其中矩阵可以应用于图像外部的相邻非现有像素)。我只是通过包裹图像来解决它(使用resp。索引模数宽度/图像高度)。



在卷积中,我使用 abs()将输出保持在正范围内。不幸的是,我不能说这是否完全正确。 (这是22年前我对大学图像处理的关注。)


I have been trying to use 2D Convolution for a project about image processing. Since i only need to convolve pixel by pixel i decided to use the following code (I know it is ugly and not optimized.) using the mathematical formula from Wikipedia:

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]

I am using Kirsch Edge Detection. Unfortunately after the convolution with just one mask the resulting image is:

After Convolution

Before Convolution

解决方案

I made a minimal complete example for convolution with which I got the algorithm running you described.

I did it the straight forward way. This is rather suitable for learning but not for serial usage (lacking any optimization for keeping the code clear and readable).

#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;
}

I compiled and tested it with VS2013 on Windows 10 as well as gcc in cygwin:

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

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

$ 

fluffyCat.64x64.ppm looks like this:

edge-detect-out.ppm looks like this:

Some notes:

I used the ancient X11 PPM format because

  1. It can be read and written with minimal code and is, thus, best fitting for such samples.
  2. It is supported in GIMP. Thus, creating and viewing is easy.

The code is inspired on Creating, Compiling, and Viewing ppm Images and, probably, cannot handle any flavor of PPM.

Attention! When GIMP saves PPM it includes a comment which the reader in the sample code cannot read. I simply removed this comment with a text editor. GIMP settings for saving: Raw data.

A common danger in such image procesing algorithms is the handling of border pixels (where matrix may be applied to neighbour non existing pixels outside the image). I simply solved it by wrapping the image around (using the resp. index modulo width/height of image).

In the convolution, I used abs() to keep output in positive range. Unfortunately, I cannot say whether this is fully correct. (It's 22 years ago since I heart about image processing in the University.)

这篇关于C中边缘检测的卷积的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆