# include # include # include # include # include "image_edge.h" /******************************************************************************/ int i4_huge ( ) /******************************************************************************/ /* Purpose: i4_huge() returns a "huge" I4. Licensing: This code is distributed under the MIT license. Modified: 29 August 2006 Author: John Burkardt Input: int I4_HUGE, a "huge" integer. */ { static int value = 2147483647; return value; } /******************************************************************************/ int *i4mat_histogram ( int m, int n, int a[], int histo_num ) /******************************************************************************/ /* Purpose: i4mat_histogram() computes a histogram of the elements of an I4MAT. Discussion: An I4MAT is an array of I4's. It is assumed that the entries in the vector A are nonnegative. Only values between 0 and HISTO_NUM will be histogrammed. Licensing: This code is distributed under the MIT license. Modified: 04 June 2010 Author: John Burkardt Input: int M, N, the order of A. int A[M*N], the array to examine. int HISTO_NUM, the maximum value for which a histogram entry will be computed. Output: int I4MAT_HISTOGRAM[HISTO_NUM+1], contains the number of entries of A with the values of 0 through HISTO_NUM. */ { int *histo_gram; int i; int j; histo_gram = ( int * ) malloc ( ( histo_num + 1 ) * sizeof ( int ) ); for ( i = 0; i <= histo_num; i++ ) { histo_gram[i] = 0; } for ( j = 0; j < n; j++ ) { for ( i = 0; i < m; i++ ) { if ( 0 <= a[i+j*m] && a[i+j*m] <= histo_num ) { histo_gram[a[i+j*m]] = histo_gram[a[i+j*m]] + 1; } } } return histo_gram; } /******************************************************************************/ int i4mat_max ( int m, int n, int a[] ) /******************************************************************************/ /* Purpose: i4mat_max() returns the maximum of an I4MAT. Discussion: An I4MAT is an MxN array of I4's, stored by (I,J) -> [I+J*M]. Licensing: This code is distributed under the MIT license. Modified: 05 June 2010 Author: John Burkardt Input: int M, the number of rows in A. int N, the number of columns in A. int A[M*N], the M by N matrix. Output: int I4MAT_MAX, the maximum entry of A. */ { int i; int j; int value; value = - i4_huge ( ); for ( j = 0; j < n; j++ ) { for ( i = 0; i < m; i++ ) { if ( value < a[i+j*m] ) { value = a[i+j*m]; } } } return value; } /******************************************************************************/ int *news ( int m, int n, int a[] ) /******************************************************************************/ /* Purpose: news() demonstrates the NEWS stencil for edge detection. Discussion: Given a black and white image A, which we regard as an M by N array of pixels, we want to produce an array E of the same shape, which contains information describing the location of edges. A simple algorithm for trying to detect edges in an array that represents an image is the NEWS scheme. For each pixel A(C), we consider its North, East, West, and South pixel neighbors. The indexing of arrays and images do not correspond, so we will use these directions instead: A(N) | | A(W)---A(C)---A(E) | | A(S) Entry E(C) of the edge array will be computed by E(C) = abs ( A(N) - A(S) ) + abs ( A(E) - A(W) ) Pixels of A that represent edges will tend to have high values of E, while pixels that are interior to a region of roughly the same shade will tend to have low values. Thus, an edge detection scheme would use the NEWS stencil to compute the E array, determine E_MAX, the maximum entry in E, choose some threshold value E_THRESH, and declare pixel A(I,J) to be associated with an edge whenever E(I,J) is greater than E_THRESH. In this program, we demonstrate the NEWS stencil using a PGM grayscale image of coins. At the end, we use the edge information to produce a color image in which the edges of the coins have been outlined in red. Licensing: This code is distributed under the MIT license. Modified: 05 June 2010 Author: John Burkardt Input: int M, N, the number of rows and columns in the image. int A[M*N], the gray scale image data, presumably integers between 0 and 255. Output: int NEWS[M*N], is 1 for each pixel that is part of an edge, and 0 otherwise. */ { int *b; int *e; int e_max; int i; int j; int thresh; /* For neatness, we add a border of zeros to the image, then fill in the border by copying the nearby original values. This will be our M+2 by N+2 data array B. */ b = ( int * ) malloc ( ( m + 2 ) * ( n + 2 ) * sizeof ( int ) ); for ( j = 0; j < n; j++ ) { for ( i = 0; i < m; i++ ) { b[i+1+(j+1)*(m+2)] = a[i+j*m]; } } for ( j = 1; j < n + 1; j++ ) { b[ 0+j*(m+2)] = b[1+j*(m+2)]; b[m+1+j*(m+2)] = b[m+j*(m+2)]; } for ( i = 1; i < m + 1; i++ ) { b[i+ 0 *(m+2)] = b[i+1*(m+2)]; b[i+(n+1)*(m+2)] = b[i+n*(m+2)]; } b[ 0+ 0 *(m+2)] = ( b[ 0+ 1 *(m+2)] + b[ 1+ 0 *(m+2)] ) / 2; b[m+1+ 0 *(m+2)] = ( b[m+1+ 1 *(m+2)] + b[m+0+ 0 *(m+2)] ) / 2; b[ 0+(n+1)*(m+2)] = ( b[ 0+(n+0)*(m+2)] + b[ 1+(n+1)*(m+2)] ) / 2; b[m+1+(n+1)*(m+2)] = ( b[m+1+(n+0)*(m+2)] + b[m+0+(n+1)*(m+2)] ) / 2; /* Apply the NEWS Operator. We do not process the boundary pixels. The picture is: | 0 +1 0 | | 0 0 0 | | 0 0 0 | + | -1 0 +1 | | 0 -1 0 | | 0 0 0 | */ e = ( int * ) malloc ( m * n * sizeof ( int ) ); for ( j = 0; j < n; j++ ) { for ( i = 0; i < m; i++ ) { e[i+j*m] = abs ( - b[i +(j+1)*(m+2)] + b[i+2+(j+1)*(m+2)] ) + abs ( - b[i+1+ j *(m+2)] + b[i+1+(j+2)*(m+2)] ); } } free ( b ); /* Remap E so the largest value is 255. */ e_max = i4mat_max ( m, n, e ); /* Threshold the data. Set the threshold to give enough detail to guess the coin denominations. */ thresh = e_max / 5; fprintf ( stdout, "\n" ); fprintf ( stdout, "NEWS:\n" ); fprintf ( stdout, " E_MAX = %d\n", e_max ); fprintf ( stdout, " Using threshold value THRESH = %d\n", thresh ); for ( j = 0; j < n; j++ ) { for ( i = 0; i < m; i++ ) { if ( e[i+j*m] < thresh ) { e[i+j*m] = 0; } else { e[i+j*m] = 1; } } } return e; } /******************************************************************************/ void pbma_write ( char *file_out_name, int xsize, int ysize, int *b ) /******************************************************************************/ /* Purpose: pbma_write() writes the header and data for an ASCII PBM file. Example: P1 # feep.pbm 24 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 1 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Licensing: This code is distributed under the MIT license. Modified: 05 June 2010 Author: John Burkardt Input: char *FILE_OUT_NAME, the name of the file. int XSIZE, YSIZE, the number of rows and columns of data. int *B, the array of XSIZE by YSIZE data values. */ { FILE *file_out; /* Open the output file. */ file_out = fopen ( file_out_name, "wt" ); if ( !file_out ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "PBMA_WRITE - Fatal error!\n" ); fprintf ( stderr, " Cannot open the output file '%s'.\n", file_out_name ); exit ( 1 ); } /* Write the header. */ pbma_write_header ( file_out, file_out_name, xsize, ysize ); /* Write the data. */ pbma_write_data ( file_out, xsize, ysize, b ); /* Close the file. */ fclose ( file_out ); return; } /******************************************************************************/ void pbma_write_data ( FILE *file_out, int xsize, int ysize, int *b ) /******************************************************************************/ /* Purpose: pbma_write_data() writes the data for an ASCII PBM file. Licensing: This code is distributed under the MIT license. Modified: 05 June 2010 Author: John Burkardt Input: ofstream &FILE_OUT, a pointer to the file. int XSIZE, YSIZE, the number of rows and columns of data. int *B, the arrays of XSIZE by YSIZE data values. */ { int i; int *indexb; int j; int numval; indexb = b; numval = 0; for ( j = 0; j < ysize; j++ ) { for ( i = 0; i < xsize; i++ ) { fprintf ( file_out, " %d", *indexb ); numval = numval + 1; indexb = indexb + 1; if ( numval % 12 == 0 || i == xsize - 1 || numval == xsize * ysize ) { fprintf ( file_out, "\n" ); } } } return; } /******************************************************************************/ void pbma_write_header ( FILE *file_out, char *file_out_name, int xsize, int ysize ) /******************************************************************************/ /* Purpose: pbma_write_header() writes the header of an ASCII PBM file. Licensing: This code is distributed under the MIT license. Modified: 05 June 2010 Author: John Burkardt Input: ofstream &FILE_OUT, a pointer to the file. char *FILE_OUT_NAME, the name of the file. int XSIZE, YSIZE, the number of rows and columns of data. */ { fprintf ( file_out, "%s\n", "P1" ); fprintf ( file_out, "# %s created by PBMA_IO::PBMA_WRITE.\n", file_out_name ); fprintf ( file_out, "%d %d\n", xsize, ysize ); return; } /******************************************************************************/ void pgma_read_data ( FILE *file_in, int xsize, int ysize, int *g ) /******************************************************************************/ /* Purpose: pgma_read_data() reads the data in an ASCII PGM file. Licensing: This code is distributed under the MIT license. Modified: 04 June 2010 Author: John Burkardt Input: FILE *FILE_IN, a pointer to the file containing the data. int XSIZE, YSIZE, the number of rows and columns of data. Output: int *G, the array of XSIZE by YSIZE data values. */ { int i; int j; int n; for ( j = 0; j < ysize; j++ ) { for ( i = 0; i < xsize; i++ ) { n = fscanf ( file_in, "%d", g ); if ( n != 1 ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "PGMA_READ_DATA - Fatal error!\n" ); fprintf ( stderr, " Unable to read data.\n" ); exit ( 1 ); } g = g + 1; } } return; } /******************************************************************************/ void pgma_read_header ( FILE *file_in, int *xsize, int *ysize, int *maxg ) /******************************************************************************/ /* Purpose: pgma_read_header() reads the header of an ASCII PGM file. Licensing: This code is distributed under the MIT license. Modified: 05 June 2010 Author: John Burkardt Input: FILE *FILE_IN, a pointer to the file. Output: int *XSIZE, *YSIZE, the number of rows and columns of data. int *MAXG, the maximum gray value. */ { # define LINE_MAX 255 int count; char *error; char line[LINE_MAX]; char *next; int step; int width; char word[LINE_MAX]; step = 0; while ( 1 ) { error = fgets ( line, LINE_MAX, file_in ); if ( !error ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "PGMA_READ_HEADER - Fatal error!\n" ); fprintf ( stderr, " End of file.\n" ); exit ( 1 ); } next = line; if ( line[0] == '#' ) { continue; } if ( step == 0 ) { count = sscanf ( next, "%s%n", word, &width ); if ( count == EOF ) { continue; } next = next + width; if ( strcmp ( word, "P2" ) != 0 && strcmp ( word, "p2" ) != 0 ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "PGMA_READ_HEADER - Fatal error.\n" ); fprintf ( stderr, " Bad magic number = \"%s\".\n", word ); exit ( 1 ); } step = 1; } if ( step == 1 ) { count = sscanf ( next, "%d%n", xsize, &width ); next = next + width; if ( count == EOF ) { continue; } step = 2; } if ( step == 2 ) { count = sscanf ( next, "%d%n", ysize, &width ); next = next + width; if ( count == EOF ) { continue; } step = 3; } if ( step == 3 ) { count = sscanf ( next, "%d%n", maxg, &width ); next = next + width; if ( count == EOF ) { continue; } break; } } return; # undef LINE_MAX }