/* This file implements the different edge detection algorithms presented in the article "A review of classic edge detectors". All functions implemented here receive only a pointer to the input image (grayscale) and the different parameters of each method. All them also return a pointer to the output image. Copyright (c) 2011-2013, Haldo Sponton Copyright (c) 2011-2013, Juan Cardelino This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include "classic_edge_detectors.h" // Useful macros #define MAX(x, y) (((x) > (y)) ? (x) : (y)) #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #define THRESHOLD(x, th) (((x) > (th)) ? (255) : (0)) // Fatal error, print a message to standard-error output and exit. static void error(char * msg) { fprintf(stderr,"Error: %s\n",msg); exit(EXIT_FAILURE); } // Memory allocation, print an error and exit if fail. static void * xmalloc(size_t size) { void * p; if( size == 0 ) error("xmalloc: zero size"); p = malloc(size); if( p == NULL ) error("xmalloc: out of memory"); return p; } // Computes a Gaussian kernel of size n x n and standard deviation sigma. static float *gaussian_kernel(int n, float sigma) { // Memory allocation for the kernel float *kernel = xmalloc(n*n*sizeof(float)); // Gaussian kernel: e^{ -(x^2+y^2) / (2sigma^2) } float sum = 0; for(int i=0; i= n-1 && j >= n-1 && i < w+n-1 && j < h+n-1 ) { aux[i+j*wx] = input[ i-n+1 + (j-n+1)*w ]; // image at center } else { if(padding_method == 0) { aux[i+j*wx] = 0.0; // zero-padding } else { // reflection boundary padding int reflex_x = i max ) max = v; } } // compute laplacian zero-crossings float *edges = xmalloc(w*h*sizeof(float)); for(int i=0; i tzc*max) || (UP_LE*DO_RI < 0.0 && abs(UP_LE-DO_RI) > tzc*max) || (DO_LE*UP_RI < 0.0 && abs(DO_LE-UP_RI) > tzc*max) || (UP*DO < 0.0 && abs(UP-DO) > tzc*max) ) { edges[i+j*w] = 255.0; } else { edges[i+j*w] = 0.0; } } } // free memory free(kernel); free(im_smoothed); free(laplacian); return edges; } // Marr-Hildreth edge detector with Laplacian of Gaussian kernel // inputs: // float *input - pointer to input image // int w, int h - width and height of input image // float sigma - gaussian standard deviation // int n - kernel size // float tzc - threshold in zero-crossing // int padding_method - padding method for convolution // output: // float * - pointer to output image float *edges_mh_log(float *im, int w, int h, float sigma, int n, float tzc, int padding_method) { // compute Laplacian using a LoG (Laplacian of Gaussian) kernel float *kernel = LoG_kernel(n,sigma); float *laplacian = conv2d(im, w, h, kernel, n, padding_method); // compute maximum of absolute Laplacian float max = 0.0; for(int i=0; i max ) max = v; } } // compute laplacian zero-crossings float *edges = xmalloc(w*h*sizeof(float)); for(int i=0; i tzc*max) || (UP_LE*DO_RI < 0.0 && abs(UP_LE-DO_RI) > tzc*max) || (DO_LE*UP_RI < 0.0 && abs(DO_LE-UP_RI) > tzc*max) || (UP*DO < 0.0 && abs(UP-DO) > tzc*max) ) { edges[i+j*w] = 255.0; } else { edges[i+j*w] = 0.0; } } } // free memory free(kernel); free(laplacian); return edges; } // Haralick edge detector // inputs: // float *input - pointer to input image // int w, int h - width and height of input image // float rhozero - threshold // int padding_method - padding method for convolution // output: // float * - pointer to output image float *edges_haralick(float *im, int w, int h, float rhozero, int padding_method) { // Haralick's masks for computing k1 to k10 // New masks calculated by 2-d fitting using LS, with the function // f(x,y) = k1 + k2*x + k3*y + k4*x^2 + k5*xy // + k6*y^2 + k7*x^3 + k8*x^2y + k9*xy^2 + k10*y^3 float mask[10][25] = { { 425, 275, 225, 275, 425, 275, 125, 75, 125, 275, 225, 75, 25, 75, 225, 275, 125, 75, 125, 275, 425, 275, 225, 275, 425}, { -2260, -620, 0, 620, 2260, -1660, -320, 0, 320, 1660, -1460, -220, 0, 220, 1460, -1660, -320, 0, 320, 1660, -2260, -620, 0, 620, 2260}, { 2260, 1660, 1460, 1660, 2260, 620, 320, 220, 320, 620, 0, 0, 0, 0, 0, -620, -320, -220, -320, -620, -2260, -1660,-1460,-1660,-2260}, { 1130, 620, 450, 620, 1130, 830, 320, 150, 320, 830, 730, 220, 50, 220, 730, 830, 320, 150, 320, 830, 1130, 620, 450, 620, 1130}, { -400, -200, 0, 200, 400, -200, -100, 0, 100, 200, 0, 0, 0, 0, 0, 200, 100, 0, -100, -200, 400, 200, 0, -200, -400}, { 1130, 830, 730, 830, 1130, 620, 320, 220, 320, 620, 450, 150, 50, 150, 450, 620, 320, 220, 320, 620, 1130, 830, 730, 830, 1130}, { -8260, -2180, 0, 2180, 8260, -6220, -1160, 0, 1160, 6220, -5540, -820, 0, 820, 5540, -6220, -1160, 0, 1160, 6220, -8260, -2180, 0, 2180, 8260}, { 5640, 3600, 2920, 3600, 5640, 1800, 780, 440, 780, 1800, 0, 0, 0, 0, 0, -1800, -780, -440, -780,-1800, -5640, -3600,-2920,-3600,-5640}, { -5640, -1800, 0, 1800, 5640, -3600, -780, 0, 780, 3600, -2920, -440, 0, 440, 2920, -3600, -780, 0, 780, 3600, -5640, -1800, 0, 1800, 5640}, { 8260, 6220, 5540, 6220, 8260, 2180, 1160, 820, 1160, 2180, 0, 0, 0, 0, 0, -2180, -1160, -820,-1160,-2180, -8260, -6220, 5540,-6220,-8260 } }; // apply the masks operators, this will lead to coefficients k1 to k10 float *aux[10]; for(int i=0; i<10; i++) { aux[i] = conv2d(im, w, h, mask[i], 5, padding_method); } // compute Haralick edges float *edges = xmalloc(w*h*sizeof(float)); // get memory for output for(int i=0; i