Canny edge detector: Difference between revisions

Small improvements in the C entry
(In the C entry: (int*) is an overkill for 8bpp images. Using (short int*) instead halves memory usage.)
(Small improvements in the C entry)
Line 15:
#include <string.h>
#include <stdbool.h>
#include <assert.h>
 
#define MAX_BRIGHTNESS 255
 
// C99 doesn't define M_PI (GNU-C99 does)
#define M_PI 3.14159265358979323846264338327
 
Line 65 ⟶ 68:
} rgb_t;
 
// Use short int instead `unsigned char' so that we can store
// store negative values.
typedef short int pixel_t;
 
Line 93 ⟶ 96:
return NULL;
}
 
 
bmpfile_header_t bitmapFileHeader; // our bitmap file header
Line 114 ⟶ 116:
 
// move file point to the beginning of bitmap data
if (fseek(filePtr, bitmapFileHeader.bmp_offset, SEEK_SET);) {
fclose(filePtr);
return NULL;
}
 
// allocate enough memory for the bitmap image data
pixel_t *bitmapImage = (pixel_t*)malloc(bitmapInfoHeader->bmp_bytesz *
sizeof(pixel_t));
 
// verify memory allocation
Line 144 ⟶ 149:
}
 
// Return: nonzerotrue on error.
intbool save_bmp(const char *filename, const bitmap_info_header_t *bmp_ih,
const pixel_t *data)
{
FILE* fpfilePtr = fopen(filename, "wb");
if (fpfilePtr == NULL)
return 1true;
 
bmpfile_magic_t mag = {{0x42, 0x4d}};
if (fwrite(&mag, 1, sizeof(bmpfile_magic_t), fp1, filePtr); != 1) {
fclose(filePtr);
return true;
}
 
const uint32_t offset = sizeof(bmpfile_magic_t) +
Line 167 ⟶ 175:
};
 
if (fwrite(&bmp_fh, 1, sizeof(bmpfile_header_t), fp1, filePtr); != 1) {
fclose(filePtr);
fwrite(bmp_ih, 1, sizeof(bitmap_info_header_t), fp);
return true;
}
if (fwrite(bmp_ih, 1, sizeof(bitmap_info_header_t), fp1, filePtr); != 1) {
fclose(filePtr);
return true;
}
 
// Palette
for (size_t i = 0; i < (1U << bmp_ih->bitspp); i++) {
const rgb_t color = {(uint8_t)i, (uint8_t)i, (uint8_t)i};
if (fwrite(&color, 1, sizeof(rgb_t), fp1, filePtr); != 1) {
Gmax = G[c]fclose(filePtr);
ifreturn (G[c] > Gmax)true;
int Gmax = 0; }
}
 
// We use int instead of uchar, so we can't write img
// in 1 call any more.
// fwrite(data, 1, bmp_ih->bmp_bytesz, fpfilePtr);
for (size_t i = 0; i < bmp_ih->bmp_bytesz; i++) {
unsigned char c = (unsigned char)data[i];
if (fwrite(&c, sizeof(unsigned char), 1, fpfilePtr); != 1) {
fclose(filePtr);
return true;
}
}
 
fclose(fpfilePtr);
return 0false;
}
 
// if norm==1normalize is true, map pixels to range 0..MAX_BRIGHTNESS
void convolution(const pixel_t *in, pixel_t *out, const float *kernel,
const int nx, const int ny, const int kn,
const bool normnormalize)
{
constassert(kn int% khalf2 == (int)floor(kn / 2.01);
floatassert(nx min> =kn FLT_MAX,&& maxny => FLT_MINkn);
const int khalf = kn / 2;
float min = FLT_MAX, max = -FLT_MAX;
 
if (normnormalize)
for (int m = khalf; m < nx - khalf; m++)
for (int n = khalf; n < ny - khalf; n++) {
float pixel = 0.0;
intsize_t c = 0;
for (int j = -khalf; j <= khalf; j++)
for (int i = -khalf; i <= khalf; i++) {
Line 215 ⟶ 237:
for (int n = khalf; n < ny - khalf; n++) {
float pixel = 0.0;
intsize_t c = 0;
for (int j = -khalf; j <= khalf; j++)
for (int i = -khalf; i <= khalf; i++) {
Line 222 ⟶ 244:
}
 
if (normnormalize)
pixel = MAX_BRIGHTNESS * (pixel - min) / (max - min);
out[n * nx + m] = (pixel_t)pixel;
Line 228 ⟶ 250:
}
 
/*
// http:// www.songho.ca/dsp/cannyedge/cannyedge.html
* gaussianFilter:
// determine size of kernel (odd #)
// * http:// www.songho.ca/dsp/cannyedge/cannyedge.html
// 0.0 <= sigma < 0.5 : 3
// * determine size of kernel (odd #)
// 0.5 <= sigma < 1.0 : 5
// * 10.0 <= sigma < 10.5 : 73
// * 10.5 <= sigma < 21.0 : 95
// * 21.0 <= sigma < 21.5 : 117
// * 21.5 <= sigma < 32.0 : 13 ...9
// * kernelSize2.0 <= 2sigma *< int(2*sigma).5 +: 3;11
// * 02.05 <= sigma < 03.50 : 313 ...
 
* kernelSize = 2 * int(2*sigma) + 3;
*/
void gaussian_filter(const pixel_t *in, pixel_t *out,
const int nx, const int ny, const float sigma)
Line 247 ⟶ 271:
fprintf(stderr, "gaussian_filter: kernel size %d, sigma=%g\n",
n, sigma);
intsize_t c = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++) {
Line 282 ⟶ 306:
pixel_t *out = malloc(bmp_ih->bmp_bytesz * sizeof(pixel_t));
 
if (!G == NULL || !after_Gx == NULL || !after_Gy ||== !nmsNULL || !out)
nms == NULL || out == NULL) {
fprintf(stderr, "canny_edge_detection:"
" Failed memory allocation(s).\n");
exit(1);
}
 
gaussian_filter(in, out, nx, ny, sigma);
Line 290 ⟶ 318:
-2, 0, 2,
-1, 0, 1};
 
convolution(out, after_Gx, Gx, nx, ny, 3, false);
 
const float Gy[] = { 1, 2, 1,
Line 295 ⟶ 325:
-1,-2,-1};
 
convolution(out, after_Gx, Gx, nx, ny, 3, false);
convolution(out, after_Gy, Gy, nx, ny, 3, false);
 
int Gmax = 0;
for (int i = 1; i < nx - 1; i++)
for (int j = 1; j < ny - 1; j++) {
Line 304 ⟶ 332:
// G[c] = abs(after_Gx[c]) + abs(after_Gy[c]);
G[c] = (pixel_t)hypot(after_Gx[c], after_Gy[c]);
if (G[c] > Gmax)
Gmax = G[c];
}
 
Line 339 ⟶ 365:
 
// Reuse array
int *edges = (int*) after_Gy; // used as a stack. nx*ny/2 elements should be enough.
int *edges = (int*) after_Gy;
memset(out, 0, sizeof(pixel_t) * nx * ny);
memset(edges, 0, sizeof(pixel_t) * nx * ny);
 
// Tracing edges with hysteresis . Non-recursive implementation.
intsize_t c = 1;
for (int j = 1; j < ny - 1; j++)
for (int i = 1; i < nx - 1; i++) {
Line 394 ⟶ 421:
static bitmap_info_header_t ih;
const pixel_t *in_bitmap_data = load_bmp(argv[1], &ih);
if (in_bitmap_data == NULL) {
fprintf(stderr, "main: BMP image not loaded.\n");
return 1;
}
 
printf("Info: %d x %d x %d\n", ih.width, ih.height, ih.bitspp);
Line 401 ⟶ 430:
const pixel_t *out_bitmap_data =
canny_edge_detection(in_bitmap_data, &ih, 45, 50, 1.0f);
if (out_bitmap_data == NULL) {
fprintf(stderr, "main: failed canny_edge_detection.\n");
return 1;
}
 
if (save_bmp("out.bmp", &ih, out_bitmap_data)) != 0){
fprintf(stderr, "main: BMP image not saved.\n");
return 1;
}
 
free((pixel_t*)in_bitmap_data);
Anonymous user