The author works as a computer and electronics engineer with the U.S. Department of Defense. He has a PhD in Electrical and Computer Engineering from Louisiana State University. His interests include computer vision, artificial intelligence, software engineering; and programming languages.
Introduction to the Series of Articles
This is the ninth in a series of articles on images and image processing. Previous articles discussed reading, writing, displaying, and printing images (TIFF format), histograms, edge detection, spatial frequency filtering, and sundry image operations. This article will discuss simple image segmentation based on histograms and image thresholding.
Image segmentation is the process of dividing an image into regions or objects. It is the first step in the field of image analysis. Image processing displays images and alters them to make them look "better," while image analysis tries to discover what is in the image.
The basic idea of image segmentation is to group individual pixels (dots in the image) together into regions if they are similar. Similar can mean they are the same intensity (shade of gray), form a texture, line up in a row, create a shape, etc. There are many techniques available for image segmentation, and they vary in complexity, power, and area of application.
Histogram-Based Image Segmentation
Histogram-based image segmentation is one of the simplest and most often used segmentation techniques. It uses the histogram to select the gray levels for grouping pixels into regions. In a simple image there are two entities: the background and the object. The background is generally one gray level and occupies most of the image. Therefore, its gray level is a large peak in the histogram. The object or subject of the image is another gray level, and its gray level is another, smaller peak in the histogram.
Figure 1 shows an image example and Figure 2 shows its histogram. The tall peak at gray level 2 indicates it is the primary gray level for the background of the image. The secondary peak in the histogram at gray level 8 indicates it is the primary gray level for the object in the image. Figure 3 shows the image of Figure 1 with all the pixels except the 8s blanked out. The object is a happy face.
This illustrates histogram-based image segmentation. The histogram will show us the gray levels of the background and the object. The largest peak represents the background and the next largest peak the object. We choose a threshold point in the valley between the two peaks and threshold the image. Thresholding takes any pixel whose value is on the object side of the point and sets it to one; it sets all others to zero. The histogram peaks and the valley between them are the keys.
The idea of histogram-based segmentation is simple, but there can be problems. Where should you set the threshold point for the image of Figure 1? If you choose the point mid-way between the two peaks (threshold point=5), you produce the image of >Figure 4. This is not the happy face object desired. If you choose the valley floor values of 4 or 5 as the threshold point, you also have a poor result. The best threshold point would be 7, but how could you know that without using trial and error?
This example is difficult because there are only ten gray levels and the object (happy face) is small. In practice, the techniques discussed below will perform adequately, but there will be problems. Automatic techniques will fail, and you may have to resort to manual methods.
Preprocessing: Histogram Equalization and Histogram Smoothing
Histogram-based segmentation depends on the histogram of the image. Therefore, you must prepare the image and its histogram before analyzing it. The first step is histogram equalization (Phillips, August 1991). Histogram equalization attempts to alter an image so its histogram is flat and spreads out over the entire range of gray levels. The result is an image with better contrast.
Photograph 1 shows an aerial image of several house trailers with its histogram. The contrast is poor and it would be very difficult to find objects based on its histogram. Photograph 2 shows the result of performing histogram equalization. The contrast is much better and the histogram is spread out over the entire range of gray levels. Photograph 3 shows the result of performing high-pass filtering (Phillips, October 1992) on the image of photograph 2. It is easy to see the house trailers, sidewalks, trees, bushes, gravel roads, and parking lots.
The next preprocessing step is histogram smoothing. When examining a histogram, you look at peaks and valleys. Too many tall, thin, peaks and deep valleys will cause problems. Smoothing the histogram removes these spikes and fills in empty canyons while retaining the same basic shape of the histogram.
Figure 5 shows the result of smoothing the histogram given in Figure 2. You can still see the peaks corresponding to the object and background, but the peaks are shorter and the valleys are filled. Photograph 4 shows another example of histogram smoothing. The histogram on the left is the original with several spikes and canyons. The histogram on the right has been smoothed. I will analyze this in the segmentation.
Smoothing a histogram is an easy operation. It replaces each point with the average of it and its two neighbors. Listing 1 shows the smooth_histogram function that performs this operation.
Thresholding and Region Growing
There are two more topics common to all the methods of image segmentation: image thresholding and region growing. Image thresholding sets the pixels in the image to one or zero. Listing 2 shows the routine threshold_image_array that accomplishes this task.
The difficult task is region growing. Figure 6 shows the result of thresholding Figure 1 correctly. The "object" in Figure 6 is a happy face. It comprises three different regions (two eyes and the smile). Region growing takes this image, groups the pixels in each separate region, and gives them unique labels. Figure 7 shows the result of region growing performed on Figure 6. Region growing grouped and labeled one eye as region one, the other eye as region two, and the smile as region three.
Figure 8 shows the algorithm for region growing. It begins with an image array g comprising zeros and pixels set to a value. The algorithm loops through the image array looking for a g(i,j) == value. When it finds such a pixel, it calls the label_and_check_neighbor routine. label_and_check_neighbor sets the pixel to g_label (the region label) and examines the pixel's eight neighbors. If any of the neighbors equal value, they are pushed onto a stack. When control returns to the main algorithm, each pixel on the stack is popped and sent to label_and_check_neighbor. All the points on the stack equaled value, so you set them and check their neighbors. After setting all the pixels in the first region, you increment g_label and move on looking for the next region.
Listing 3 shows the functions that implement region growing with grow being the primary routine. grow runs through the region-growing algorithm and calls label_and_check_neighbor shown next in Listing 3.
The grow and label_and_check_neighbor functions follow the region-growing algorithm step for step. The only unusual item is the stack. There are several ways to implement a stack. I used a simple array and a file. I did this because a region could be as large as ROWSxCOLS (10,000 in the C Image Processing System), and the stack must be large enough to hold that many points. I push points onto the stack array by putting them in the array and moving the top of stack pointer. When the stack array becomes full, I write it to the stack file. If the array fills again, I push the array onto the top of the stack file. I pop points off the stack array by reading them and decrementing the top of stack pointer. When the stack array is empty, I pop an array off the stack file. The final four functions of Listing 3 (push_data_onto_stack_file, pop_data_off_of_stack_file, append_stack_files, and copy_stack_files) implement the push and pop functions for the stack array and file.
Histogram-Based Segmentation Techniques
There are four segmentation techniques: the manual technique, histogram peak technique, histogram valley technique, and an adaptive technique.
The Manual Technique
In the manual technique the user inspects an image and its histogram manually. Trial and error come into play and the result is as good as you want it to be.
I used Photograph 4 as the input for all the segmentation examples. Photograph 5, Photograph 6, and Photograph 7 show the result of segmentation using three different thresholds. The result in Photograph 5 used a high of 255 and a low of 125. The segmentation included the white gravel roads as well as the house trailers and sidewalks. The result in Photograph 6 used a high of 255 and a low of 175. The gravel roads begin to disappear, but the house trailers and sidewalks remain. Photograph 7 shows the result using a high of 255 and a low of 225. This segmentation only finds the four dominant house trailers. Which answer is correct? That depends on what you wanted to find.
Note, all image segmentations will appear rough. You can perform additional processing to make the result more pleasing to the eye, but that is not the purpose of segmentation. The purpose is to break the image into pieces so later computer processing can interpret their meaning. The output is for computer not human consumption. Also note how difficult it is for the computer, even with manual aid, to find objects that are trivial for humans to see. Anyone could trace over the input image and outline the objects better than the segmentation process.
Listing 4 shows the code that implements manual segmentation. The function manual_threshold_segmentation has the same form as the other application functions in this series. It creates the output image file if it does not exist, reads in the input data, thresholds it (Listing 2) , grows regions if requested (Listing 3) , and writes the output.
manual_threshold_segmentation has the usual inputs (image file names, image arrays, etc.) as well as the high and low threshold values, and the value and segment parameters. The value parameter specifies the value at which to set a pixel if it falls between the high and low thresholds. You usually set value equal to 1 since those pixels outside the high-low range are set to zero. The segment parameter specifies whether or not to grow regions after thresholding. Sometimes you only want to threshold an image and not grow regions. The two operations are identical except for the last step. If segment == 1, you call the region-growing routines.
Manual segmentation is good for fine tuning and getting a feel for the operation. Its trial-and-error nature, however, makes it time consuming and impractical for many applications. You need techniques that examine the histogram and select threshold values automatically.
Histogram Peak Technique
The first such technique uses the peaks of the histogram. This technique finds the two peaks in the histogram corresponding to the background and object of the image. It sets the threshold half way between the two peaks. Look back at the smoothed histogram in Figure 5. The background peak is at 2 and the object peak is at 7. The midpoint is 4, so the low threshold value is 4 and the high is 9.
The peaks technique is straightforward except for two items. In the histogram in Figure 5, you'll note the peak at 7 is the fourth highest peak. The peaks at 1 and 3 are higher, but they are part of the background mountain of the histogram and do not correspond to the object. When you search the histogram for peaks, you must use a peak spacing to ensure the highest peaks are separated. If you did not, then you would choose 2 as the background peak and 1 as the object peak. Figure 9 shows the disastrous effect of this.
The second item to watch carefully is determining which peak corresponds to the background and which corresponds to the object. Suppose an image had the histogram shown in Figure 10. Which peak corresponds to the background? The peak for gray level 8 is the highest, but it corresponds to the object not the background. The reason is the mountain surrounding the peak at gray level 2 has a much greater area than that next to gray level 8. Therefore, gray levels 0 through 6 occupy the vast majority of the image, and they are the background.
Listing 5 shows the source code to implement the peaks technique. peak_threshold_segmentation is the primary function. It checks for the existence of the output image, reads the input image, and calculates and smoothes the histogram. Next, it calls new functions to find the histogram peaks and determine the high and low threshold values. Finally, it thresholds the image, performs region growing if desired, and writes the result image to the output file.
The functions find_peaks and insert_into_peaks in Listing 5 analyze the histogram to find the peaks for the object and background. These functions build a list of histogram peaks. There are several ways to do this. I used an array of values. find_peaks loops through the histogram and calls insert_into_peaks, which puts the histogram values in the proper place in the array. find_peaks ends by looking at the spacing between the largest peaks to ensure we do not have a disaster such as shown in Figure 9.
The function peaks_high_low takes the two peaks from find_peaks and calculates the high- and low-threshold values for segmentation. peaks_high-low examines the mountains next to the peaks as illustrated in Figure 10. It then finds the mid-point between the peaks and sets the high and low threshold values.
Photograph 8 shows the result of applying the peaks technique to the image of Photograph 4. The peaks technique found the two peaks at 255 and 77. The mid-point is 166, so the high threshold is 255 and the low threshold is 166. This is a reasonably good segmentation of Photograph 4.
Histogram Valley Technique
The second automatic technique uses the peaks of the histogram, but concentrates on the valley between them. Instead of setting the mid-point arbitrarily half way between the two peaks, the valley technique searches between the two peaks to find the lowest valley.
Look back at the histogram of Figure 10. The peaks are at gray levels 2 and 8 and the peaks technique would set the midpoint at 5. In contrast, the valley technique searches from 2 through 8 to find the lowest valley. In this case, the "valleypoint" is at gray level 7.
Listing 6 shows the code that implements the valley technique. The primary function is valley_threshold_segmentation. It checks for the output file, reads the input image, calculates and smoothes the histogram, and finds the peaks as peak_threshold_segmentation did. It finds the valley-point via the functions valley_high_low, find_valley_point, and insert_into_deltas. find_valley_point starts at one peak and goes to the next inserting the histogram values into a deltas array via the insert_into_deltas function. This uses an array to create a list of deltas in the same manner as insert_into_peaks did in Listing 5. Once you have the valleypoint, valley_high_low checks the mountains around the peaks to ensure you associate the peaks with the background and object correctly.
Photograph 9 shows the result of applying the valley technique to the image in Photograph 4. It found the peaks at 77 and 255 and went from 77 up to 255 looking for the lowest valley. It pinpointed the lowest valley at gray level 241.
Adaptive Histogram Technique
The final technique uses the peaks of the histogram in a first pass and adapts itself to the objects found in the image in a second pass (Castleman 1979). In the first pass, the adaptive technique calculates the histogram for the entire image. It smoothes the histogram and uses the peaks technique to find the high and low threshold values.
In the second pass, the technique works on each ROWSxCOLS area of the image individually. In each area, it segments using the high and low values found during the first pass. Then, it calculates the mean value for all the pixels segmented into background and object. It uses these means as new peaks and calculates new high and low threshold values for that ROWSxCOLS area. Now, it segments that area again using the new values.
Listing 7 shows the code that implements the adaptive technique with adaptive_threshold_segmentation being the primary function. It is very similar to the peak_threshold_segmentation function of Listing 5 in that it uses all that code for its first pass. The second pass starts by calling threshold_and_find_means. This function thresholds the image array into background and object and calculates the mean pixel value for each. The second pass continues by using peaks_high_low to find new threshold values based on the background and object means. Finally, you threshold the image using these new threshold values.
Photograph 10 shows the result of applying the adaptive technique to the image of Photograph 4. The first pass found the high- and low-threshold values to be 255 and 166. On the left side of the photograph, the second pass thresholded the image array and found the background mean to be 94 and the object mean to be 205. The new threshold values were 255 and 149. On the right side of the photograph, the background and object means were 84 and 200 and the new threshold values were 255 and 142.
Integrating Histogram-Based Segmentation into the C Image Processing System
Listing 8 shows the new code for the main routine of CIPS. I've added the options of thresholding and segmentation using the four techniques discussed above in cases 16 and 17. Listed next are the changes to the CIPS main menu and the two functions that interact with the user to obtain the processing options.
Listing 9 shows a stand-alone application program for thresholding and segmenting entire image files. It is command- line driven and calls the functions shown in the earlier listings.
Conclusions
This installment in the series introduced image segmentation. This is the first step in locating and labeling the contents of an image. The techniques discussed work on simple images with good contrast and gray level separation between the object and background. You will need other techniques to attack more complex images.
References
Castleman, Kenneth R. 1979. Digital Image Processing. Prentice-Hall.
Phillips, Dwayne. August 1991. "Image Processing, Part 4: Histograms and Histogram Equalization," The C Users Journal.
Phillips, Dwayne. October 1992. "Image Processing, Part 7: Spatial Frequency Filtering," The C Users Journal.
Figure 1 An image example
22222232221222212222 32222321250132123132 22588897777788888232 12988877707668882122 22888892326669893213 21278221222666665222 22002222220226660225 21221231223266622321 32238852223266821222 21288888342288882232 22328888899888522121 22123988888889223422 23222278888882022122 22232323883212123234 25221212222222222222 22122222320222202102 20222322412212223221 22221212222222342222 21222222221222222142Figure 2 A histogram of the image in Figure 1
Figure 3 The image in Figure 1 with all the pixels except the 8's blanked out
-------------------- -------------------- ---888------88888--- ---888-------888---- --8888--------8----- ----8--------------- -------------------- -------------------- ----88--------8----- ---88888----8888---- ----88888--888------ ------8888888------- -------888888------- --------88---------- -------------------- -------------------- -------------------- -------------------- --------------------Figure 4 Figure 1 with athreshold point of 5
00000000000000000000 00000000000000000000 00011111111111111000 00111111101111110000 00111110001111110000 00011000000111110000 00000000000001110000 00000000000011100000 00001100000011100000 00011111000011110000 00001111111111000000 00000111111111000000 00000011111110000000 00000000110000000000 00000000000000000000 00000000000000000000 00000000000000000000 00000000000000000000 00000000000000000000Figure 5 The result of smoothing the histogram given in Figure 2
Figure 6 The result of correctly thresholding Figure 1
00000000000000000000 00000000000000000000 00011100000011111000 00011100000001110000 00111100000000100000 00001000000000000000 00000000000000000000 00000000000000000000 00001100000000100000 00011111000011110000 00001111100111000000 00000011111110000000 00000001111110000000 00000000110000000000 00000000000000000000 00000000000000000000 00000000000000000000 00000000000000000000 00000000000000000000Figure 7 The result of region growing performed on Figure 6
00000000000000000000 00000000000000000000 00011100000022222000 00011100000002220000 00111100000000200000 00001000000000000000 00000000000000000000 00000000000000000000 00003300000000300000 00033333000033330000 00003333300333000000 00000033333330000000 00000003333330000000 00000000330000000000 00000000000000000000 00000000000000000000 00000000000000000000 00000000000000000000 00000000000000000000Figure 8 Pseudocode for region growing
1. Given an image g with m rows and n columns g(i,j) for i=1,m j=1,n g(i,j) = value for object = 0 for background 2. set g_label=2 this is the label value 3. for (i=0; i<m; i++) scan ith row for (j=0; j<n; j++) check jth element stack_empty = true if g(i,j) == value label_and_check_neighbor(g(i,j),g_label) while stack_empty = false do pop element (i,j) off the stack label_and_check_neighbor(g(i,j),g_label) end while g_label = g_label + 1 end of checking jth element end of scanning ith row 4. The End --------------------------------------- procedure label_and_check_neighbor(g(r,e), g_label) g(r,e) = g_label for (R=r-1; r<=r+1; R++) for (E=e-1; e<=e+1; e++) if g(R,E) == value then push (R,E) onto the stack stack_empty = false end if end loop over E end loop over R end procedure label_and_check_neighborFigure 9 Result of incorrectpeak separation when applying the histogram peak technique to Figure 1
----------*----*---- -------*--**--*--*-- -------------------- *--------*-------*-- ------------------*- -*-----*------------ --**------*-----*--- -*--*--*-----------* ----------------*--- -*------------------ -----------------*-* --*----------------- --------------*--*-- ------------*-*----- ----*-*------------- --*-------*----*-**- -*-------*--*------* ----*-*------------- -*--------*------*--Figure 10 A histogram in which the highest peak does not correspond to the background
Photograph 1 Aerial image with poor contrast and histogram
Photograph 2 Result of histogram on Photograph 1
Photograph 3 Result of high-pass filtering on Photograph 2
Photograph 4 Image portion with histogram and smoothed histogram
Photograph 5 Threshold of Photograph 4 withhigh=225 and low=125
Photograph 6 Threshold of Photograph 4 withhigh=255 and low=175
Photograph 7 Threshold of Photograph 4 withhigh=255 and low=225
Photograph 8 Threshold of >Photograph 4 using peaks technique (high=255, low=166)
Photograph 9 Threshold of >Photograph 4 using valleytechnique (high=255, low=241)
Photograph 10 Threshold of Photograph 4 using adaptive technique (high=255, low=149 for left side; high=255, low=142 for right side)
Listing 1 Function for smoothing a histogram
/********************************************* * * smooth_histogram(... * * This function smoothes the input histogram * and returns it. It uses a simple averaging * scheme where each point in the histogram * is replaced by the average of itself and * the two points on either side of it. * *********************************************/ smooth_histogram(histogram) unsigned long histogram[]; { int i; unsigned long new_hist[GRAY_LEVELS+1]; zero_histogram(new_hist); new_hist[0] = (histogram[0] + histogram[1])/2; new_hist[GRAY_LEVELS] = (histogram[GRAY_LEVELS] + histogram[GRAY_LEVELS-1])/2; for(i=1; i<GRAY_LEVELS; i++){ new_hist[i](histogram[i-1] + histogram[i] + histogram[i+1])/3; } for(i=0; i<=GRAY_LEVELS; i++) histogram[i] = new_hist[i]; } /* ends smooth_histogram */ /* End of File */
Listing 2 Function for image thresholding
/********************************************* * * threshold_image_array(... * * This function thresholds an input image array * and produces a binary output image array. * If the pixel in the input array is between * the hi and low values, then it is set to value. * Otherwise, it is set to 0. * *********************************************/ threshold_image_array(in_image, out_image, hi, low, value) short hi, low, in image[ROWS][COLS], out_image[ROWS][COLS], value; { int counter = 0, i, j; for(i=0; i<ROWS; i++){ for(j=0; j<COLS; j++){ if(in_image[i][j] >= low && in_image[i][j] <= hi){ out_image[i][j] = value; counter++; } else out_image[i][j] = 0; } /* ends loop over j */ ) /* ends loop over i */ printf("\n\tTIA> set %d points", counter); } /* ends threshold_image_array */ /* End of File */
Listing 3 Functions that implement region growing
/********************************************* * grow(... * This function is an object detector. * Its input is an binary image array * containing 0's and value's. * It searches through the image and connects * the adjacent values. **********************************************/ grow(binary, value) short binary[ROWS][COLS], value; { char name[80]; int first_call, i, j, object_found, pointer, pop_i, pop_j, stack_empty, stack_file_in_use; short g_label, stack[STACK_SIZE][2]; /*********************************** * Now begin the process of growing * regions. *************************************/ g_label = 2; object_found = 0; first_call = 1; for(i=0; i<ROWS; i++){ for(j=0; j<COLS; j++){ stack file in use = 0; stack_empty = 1; pointer = -1; /********************************* * Search for the first pixel of * a region. **********************************/ if(binary[i][j] == value){ label_and_check_neighbor(binary, stack, g_label, &stack_empty, &pointer, i, j, value, &stack_file_in_use, &first_call); object_found = 1; } /* ends if binary[i]j] == value */ /****************************** * If the stack is not empty, * pop the coordinates of * the pixel off the stack * and check its 8 neighbors. *******************************/ while(stack_empty == 0){ pop_i = stack[pointer][0]; /* POP */ pop_j = stack[pointer][1]; /* OPERATION */ --pointer; if(pointer <= 0){ if(stack_file_in_use){ pop_data_off_of_stack_file( stack, &pointer, &stack_file_in_use); } /* ends if stack_file_in_use */ else{ pointer = 0; stack_empty = 1; } /* ends else stack file is not in use */ } /* ends if point <= 0 */ label_and_check_neighbor(binary, stack, g_label, &stack_empty, &pointer, pop_i, pop_j, value, &stack_file_in_use, &first_call); } /* ends while stack_empty == 0 */ if(object_found == 1){ object_found = 0; ++g_label; } /* ends if object_found == 1 */ } /* ends loop over j */ } /* ends loop over i */ printf("\nGROW> found %d objects", g_label); } /* ends grow */ /******************************************** * label_and_check_neighbors(... * This function labels a pixel with an object * label and then checks the pixel's 8 * neighbors. If any of the neigbors are * set, then they are also labeled. **********************************************/ label_and_check_neighbor(binary_image, stack, g_label, stack_empty, pointer, r, e, value, stack_file_in_use, first_call) int e, *first_call, *pointer, r, *stack_empty, *stack_file_in_use; short binary_image [ROWS] [COLS], g_label, stack[STACK_SIZE][2], value; { int already_labeled = 0, i, j; if (binary_image[r][e] == g_label) already_labeled = 1; binary_image[r][e] = g_label; /************************************* * Look at the 8 neighors of the * point r,e. * Ensure the points you are checking * are in the image, i.e. not less * than zero and not greater than * ROWS-1 or COLS-1. **************************************/ for(i=(r-1); i<=(r+1); i++){ for(j=(e-1); j<=(e+1); j++){ if((i>=0) && (i<=ROWS-1) && (j>=0) && (j<=COLS-1)) { if(binary_image[i][j] == value){ *pointer = *pointer + 1; stack[*pointer][0] = i; /* PUSH */ stack[*pointer][1] = j; /* OPERATION */ *stack_empty = 0; if(*pointer >= (STACK_SIZE - STACK_FILE_LENGTH)){ push_data_onto_stack_file(stack, pointer, first_call); *stack_file_in_use = 1; } /* ends if *pointer >= STACK_SIZE - STACK_FILE_LENGTH*/ } /* end of if binary_image == value */ } /* end if i and j are on the image */ } /* ends loop over i rows */ } /* ends loop over j columns */ } /* ends label_and_check_neighbors */ /*************************************** * push_data_onto_stack_file(... * This function takes the stack array * and pushes it onto the stack file. *****************************************/ push_data_onto_stack_file(stack, pointer, first_call) int *first_call, *pointer; short stack[STACK_SIZE][2]; { char backup_file_name[MAX_NAME_LENGTH]; FILE *backup_file_pointer, *stack_file_pointer; int diff, i; short holder[STACK_FILE_LENGTH][2]; printf("\nSFO> Start of push_data_onto_stack "); diff = STACK_SIZE - STACK_FILE_LENGTH; /***************************************** * Copy the elements to be stored to the * stack file into holder ******************************************/ for(i=0; i<STACK_FILE_LENGTH; i++){ holder[i][0] = stack[i][0]; holder[i][1] = stack[i][1]; } /***************************************** * Move the elements of the stack down *****************************************/ for(i=0; i<diff; i++){ stack[i][0] = stack[i + STACK_FILE_LENGTH][0]; stack[i][1] = stack[i + STACK_FILE_LENGTH][1]; } /***************************************** * Fill the top of the stack with zeros *****************************************/ for(i=diff; i<STACK_SIZE; i++){ stack[i][0] = 0; stack[i][1] = 0; } *pointer = *pointer - STACK_FILE_LENGTH; /************************************************* * Store the holder array into the stack file. * Open the stack file for writing in binary * mode. If the file does not exist it will be * created. If the file does exist it will be * over written. * PUSH - IF first_time == 1 then write to stack * ELSE write to stack.bak * append stack onto stack.bak * copy stack.bak to stack * this has the effect of writing * to the beginning of the stack. ************************************************/ if(*first_call == 1){ *first_call = *first_call + 1; if((stack_file_pointer = fopen(STACK_FILE,"wb")) == NULL) printf("\nSFO> Could not open stack file"); else{ /*printf("\n\nSFO> Writing to stack file");*/ fwrite(holder, sizeof(holder), 1, stack_file_pointer); fclose(stack_file_pointer); } /* ends else could not open stack_file */ } /* ends if *first_call == 1 */ else{ /* else stack file has been used already */ strcpy(backup_file_name, STACK_FILE); append_string(".bak\0", backup_file_name); if((backup_file_pointer = fopen(backup_file_name, "wb")) == NULL) printf("\nSFO> Could not open backup file"); else{ /*printf("\n\nSFO> Writing to backup file");*/ fwrite(holder, sizeof(holder), 1, backup_file_pointer); fclose(backup_file_pointer); } /* ends else could not open backup_file */ append_stack_files(backup_file_name, STACK_FILE, holder); copy_stack_files(backup_file_name, STACK_FILE, holder); } /* ends else first_call != 1 */ printf("--- End of push_data_onto_stack"); } /* ends push_data_onto_stack_file */ /*************************************** * pop_data_off_of_stack_file(... * This function pops the stack array * off of the stack file. ****************************************/ pop_data_off_of_stack_file(stack, pointer, stack_file_in_use) int *pointer, *stack_file_in_use; short stack[STACK_SIZE][2]; { char backup_file_name[MAX_NAME_LENGTH]; FILE *backup_file_pointer, *stack_file_pointer; int i; long write_counter; short holder[STACK_FILE_LENGTH][2], holder2[STACK_FILE_LENGTH][2]; /******************************************** * POP - Read 1 time from stack * Copy the remainder of stack to * stack.bak * Copy stack.bak to stack * This has the effect of popping off * of the stack. * Read the holder array from the stack file. * Open the stack file for reading in binary * mode. * If it requires more than one write to * copy the remainder of stack to * stack.bak then there is still data in the * stack file so set stack_file_in_use = 1. * Else set it to 0. ***********************************************/ printf("\nSFO> Start of pop_data_off_of_stack "); write_counter = 0; strcpy(backup_file_name, STACK_FILE); append_string(".bak\0", backup_file_name); if( (stack_file_pointer = fopen(STACK_FILE, "rb")) == NULL) printf("\nSFO> Could not open stack file"); else{ /*printf("\n\nSFO> Reading from stack file");*/ fread(holder, sizeof(holder), 1, stack_file_pointer); backup_file pointer = fopen(backup_file_name, "wb"); while( fread(holder2, sizeof(holder2), 1, stack_file pointer) ){ fwrite(holder2, sizeof(holder2), 1, backup_file_pointer); ++write_counter; } /* ends while reading */ if(write_counter > 0) *stack_file_in_use = 1; else *stack_file_in_use = 0; fclose(backup_file_pointer); fclose(stack_file_pointer); } /* ends else could not open stack file */ copy_stack_files(backup_file_name, STACK_FILE, holder2); for(i=0; i<STACK_FILE_LENGTH; i++){ stack[i][0] = holder[i][0]; stack[i][1] = holder[i][1]; } *pointer = *pointer + STACK_FILE_LENGTH - 1; printf("--- End of pop_data_off_of_stack"); } /* ends pop_data_off_of_stack_file */ /********************************************* * append_stack_files(... * Append the second file onto the end * of the first. ***********************************************/ append_stack_files(first_file, second_file, holder) char first_file[], second file[]; short holder[STACK_FILE_LENGTH][2]; { FILE *first, *second; int i; if((first = fopen(first_file, "r+b")) == NULL) printf("\n\nSFO> Cannot open file %s", first_file); if((second = fopen(second_file, "rb")) == NULL) printf("\n\nSFO> Cannot open file %s", second_file); /*************************************** * Seek to the end of the first file and * to the beginning of the second file. *****************************************/ fseek(first, OL, 2); fseek(second, OL, 0); while(fread(holder, sizeof(holder), 1, second) ){ fwrite(holder, sizeof(holder), 1, first); } /* ends while reading */ fclose(first); fclose(second); } /* ends append_stack_files */ /****************************************** * copy_stack_files(... * Copy the first file to the second. ********************************************/ copy_stack_files(first_file, second_file, holder) char first_file[], second_file[]; short holder[STACK_FILE_LENGTH][2]; { FILE *first, *second; int i; if( (first = fopen[first_file, "rb")) == NULL) printf("\n\nSFO> Cannot open file %s", first_file); if( (second = fopen(second_file, "wb")) == NULL) printf("\n\nSFO> Cannot open file %s", second_file); /**************************************** * Seek to the beginning of the first file. *****************************************/ fseek(first, 0L, 0); while( fread(holder, sizeof(holder), 1, first) ){ fwrite(holder, sizeof(holder), 1, second); } /* ends while reading */ fclose(first); fclose(second); } /* ends copy_stack_files */ /* End of File */
Listing 4 Code implementing manual segmentation
/************************************************** * manual_threshold_segmentation(... * This function segments an image using thresholding * given the hi and low values of the threshold * by the calling routine. It reads in an image * and writes the result to the output image. * If the segment parameter is 0, you only * threshold the array - you do not segment. ***************************************************/ manual_threshold_segmentation(in_name, out_name, the_image, out_image, il, ie, ll, le, hi, low, value, segment) char in_name[], out_name[]; int il, ie, ll, le, segment; short hi, low, the_image[ROWS][COLS], out_image[ROWS][COLS], value; { int length, width; struct tiff_header_struct image_header; if(does_not_exist(out_name)){ printf("\n\nMTS> output file does not exist %s", out_name); read_tiff_header(in_name, &image_header); round_off_image_size(&image_header, &length, &width); image_header.image_length = length*ROWS; image_header.image_width = width*COLS; create_allocate_tiff_file(out_name, &image_header, out_image); } /* ends if does_not_exist */ read_tiff_image(in_name, the_image, il, ie, ll, le); threshold_image_array(the_image, out_image, hi, low, value); if(segment == 1) grow(out_image, value); write_array_into_tiff_image(out_name, out_image, il, ie, ll, le); } /* ends manual_threshold_segmentation */ /* End of File */
Listing 5 Code implementing the histogram peaks technique
/********************************************* * peak_threshold_segmentation(... * This function segments an image using * thresholding. It uses the histogram peaks * to find the hi and low values of the * threshold. * If the segment parameter is 0, you only * threshold the array - you do not segment. ***********************************************/ peak_threshold_segmentation(in_name, out_name the_image, out_image, il, ie, ll, le, value, segment) char in name[], out_name[]; int il, ie, ll, le, segment; short the_image[ROWS][COLS], out_image[ROWS] [COLS], value; { int length, peak1, peak2, width; short hi, low; struct tiff_header_struct image_header; unsigned long histogram[GRAY_LEVELS+1]; if(does_not_exist(out_name)){ printf("\n\nPTS> output file does not exist %s", out_name); read_tiff_header(in_name, &image_header); round_off_image_size(&image_header, &length, &width); image_header.image_length = length*ROWS; image_header.image_width = width*COLS; create_allocate_tiff_file(out_name, &image_header, out_image); } /* ends if does not exist */ read_tiff_image(in_name, the_image, il, ie, ll, le); zero_histogram(histogram); calculate_histogram(the_image, histogram); smooth_histogram(histogram); find_peaks(histogram, &peak1, &peak2); peaks_high_low(histogram, peak1, peak2, &hi, &low); threshold_image_array(the_image, out_image, hi, low, value); if(segment == 1) grow(out_image, value); write_array_into_tiff_image(out_name, out_image, il, ie, ll, le); } /* ends peak_threshold_segmentation */ /******************************************* * find_peaks(... * This function looks through the histogram * array and finds the two highest peaks. * The peaks must be separated, cannot be * next to each other, by a spacing defined * in cips.h. * The peaks array holds the peak value * in the first place and its location in * the second place. *******************************************/ find_peaks(histogram, peak1, peak2) unsigned long histogram[]; int *peak1, *peak2; { int distance[PEAKS], peaks[PEAKS][2]; int i, j=0, max:0, max_place:0; for(i=0; i<PEAKS; i++){ distance[i] = 0; peaks[i][0] = -1; peaks[i] [1] = -1; } for(i=0; i<=GRAY_LEVELS; i++){ max = histogram[i]; max_place = i; insert_into_peaks(peaks, max, max_place); } /* ends loop over i */ for(i=1; i<PEAKS; i++){ distance[i] = peaks[0][1] - peaks[i][1]; if(distance[i] < 0) distance[i] = distance[i]*(-1); } *peak1 = peaks[0] [1]; for(i=PEAKS-1; i>0; 1--) if(distance[i] > PEAK_SPACE) *peak2 = peaks[i][1]; } /* ends find_peaks */ /******************************************* * insert_into_peaks(... * This function takes a value and its * place in the histogram and inserts them * into a peaks array. This helps us rank * the the peaks in the histogram. * The objective is to build a list of * histogram peaks and thier locations. * The peaks array holds the peak value * in the first place and its location in * the second place. *******************************************/ insert_into_peaks(peaks, max, max_place) int max, max_place, peaks[PEAKS][2]; } int i, j; /* first case */ if(max > peaks[0][0]){ for(i=PEAKS-1; i>0; i--){ peaks[i][0] = peaks[i-1][0]; peaks[il[1] = peaks[i-1[1]; } peaks[0] [0] = max; peaks[0][1] = max_place; } /* ends if */ /* middle cases */ for(j=0; j<PEAKS-3; j++){ if(max < peaks[j][0] && max > peaks[j+1][0]){ for(i:PEAKS-1; i>j+1; i--){ peaks[i][0] = peaks[i-1][0]; peaks[i][1] = peaks[i-1][1]; } peaks[j+1][0] = max; peaks[j+1][1] = max_place; } /* ends if */ } /* ends loop over j */ /* last case */ if(max < peaks[PEAKS-2][0] && max > peaks[PEAKS-1] [0]){ peaks[PEAKS-1][0] = max; peaks[PEAKS-1][0] = max_place; } /* ends if */ } /* ends insert into_peaks */ /*********************************************** * peaks_high_low(... * This function uses the histogram array * and the peaks to find the best high and * low threshold values for the threshold * function. You want the hi and low values * so that you will threshold the image around * the smaller of the two "humps" in the * histogram. This is because the smaller * hump represents the objects while the * larger hump represents the background. ***********************************************/ peaks_high_low(histogram, peak1, peak2, hi, low) int peak1, peak2; short *hi, *low; unsigned long histogram[]; { int i, mid_point; unsigned long sum1 = 0, sum2 = 0; if(peak1 > peak2) mid_point = ((peak1 - peak2)/2) + peak2; if(peak1 < peak2) mid_point = ((peak2 - peak1)/2) + peak1; for(i=0; i<mid_point; i++) sum1 = sum1 + histogram[i]; for(i=mid_point; i<=GRAY_LEVELS; i++) sum2 = sum2 + histogram[i]; if(sum1 >= sum2){ *low = midpoint; *hi = GRAY_LEVELS; } else{ *low = 0; *hi = mid_point; } } /* ends peaks_high_low */ /* End of File */
Listing 6 Code implementing the histogram valley technique
/********************************************* * valley_threshold_segmentation(... * This function segments an image using * thresholdlng. It uses the histogram valleys * to find the hi and low values of the * threshold. * If the segment parameter is 0, you only * threshold the array - you do not segment. ***********************************************/ valley_threshold_segmentation(in_name, out_name, the_image, out_image, il, ie, ll, le, value, segment) char in_name[], out_name[]; int il, ie, ll, ie, segment; short the_image[ROWS] [COLS], out_image[ROWS][COLS], value; { int length, peak1, peak2, width; short hi, low; struct tiff_header_struct image_header; unsigned long-histogram[GRAY_LEVELS+1]; if(does_not_exist(out_name)){ printf{"\n\nVTS> output file does not exist %s", out_name); read_tiff_header(in_name, &image_header); round_off_image_size(&image_header, &length, &width); image_header.image_length = length*ROWS; image_header.image_width = width*COLS; create_allocate tiff_file(out_name, &image_header, out_image); } /* ends if does_not_exist */ read_tiff_image(in_name, the_image, il, ie, ll, ie); zero_histogram(histogram); calculate_histogram(the_image, histogram); smooth_histogram(histogram); find_peaks(histogram, &peak1, &peak2); valley_high_low(histogram, peak1, peak2, &hi, &low); threshold_image_array(the_image, out_image, hi, low, value); if(segment == 1) grow(out_image, value); write_array_into_tiff_image(out_name, out_image, il, ie, ll, le); } /* ends valley_threshold_segmentation */ /****************************************** * valley_high_low(... * This function uses the histogram array * and the valleys to find the best high and * low threshold values for the threshold * function. You want the hi and low values * so that you will threshold the image around * the smaller of the two "humps" in the * histogram. This is because the smaller * hump represents the objects while the * larger hump represents the background. *******************************************/ valley_high_low(histogram, peak1, peak2, hi, low) int peak1, peak2; short *hi, *low; unsigned long histogram[]; { int i, valley_point; unsigned long sum1 = 0, sum2 = 0; find_valley_point(histogram, peak1, peak2, &valley_point); /*printf("\nVHL> valley point is %d", valley_point);*/ for(i=0; i<valley_point; i++) sum1 = sum1 + histogram[i]; for(i=valley_point; i<=GRAY LEVELS; i++) sum2 = sum2 + histogram"[i]; if(sum1 >= sum2){ *low = valley point; *hi = GRAY_LEVELS; } else{ *low = 0; *hi = valley_point; } } /* ends valley_high low */ /****************************************** * find_valley_point(... * This function finds the low point of * the valley between two peaks in a * histogram. It starts at the lowest * peak and works its way up to the * highest peak. Along the way, it looks * at each point in the histogram and inserts * them into a list of points. When done, * it has the location of the smallest histogram * point - that is the valley point. * The deltas array holds the delta value * in the first place and its location in * the second place. *******************************************/ find_valley_point(histogram, peak1, peak2, valley_point) int peak1, peak2, *valley_point; unsigned long histogram[]; { int deltas[PEAKS][2], delta_hist, i; for(i=0; i<PEAKS; i++){ deltas[i][0] = 10000; deltas[i][1] = -1; if(peak1 < peak2){ for(i=peak1+1; i<peak2; i++){ delta_hist = (int)(histogram[i]); insert_into_deltas(deltas, delta_hist, i); } /* ends loop over i */ } /* ends if peak1 < peak2 */ if(peak2 < peak1){ for(i=peak2+1; i<peak1; i++){ delta_hist = (int)(histogram[i]); insert_into_deltas(deltas, delta_hist, i); } /* ends loop over i */ } /* ends if peak2 < peak1 */ *valley_point = deltas[0][1]; } /* ends find valley_point */ /**************************************** * insert_into_deltas(... * This function inserts histogram deltas * into a deltas array. The smallest delta * will be at the top of the array. * The objective is to build a list of * histogram area deltas and thier locations. * The deltas array holds the delta value * in the first place and its location in * the second place. *******************************************/ insert_into_deltas(deltas, value, place) int value, place, deltas[PEAKS] [2]; { int i, j; /* first case */ if(value < deltas[0] [0]){ for(i=PEAKS-1; i>0; i--){ deltas[i][0] = deltas[i-1][[0]; deltas[i] [1] = deltas[i-1] [1]; } deltas[0] [0] = value; deltas[0][1] = place; } /* ends if */ /* middle cases */ for(j=0; j<PEAKS-3; j++){ if(value > deltas[j][0] && value < deltas [j+1] [0] ) { for(i=PEAKS-1; i>j+1; i--){ deltas[i][0] = deltas[i-1][0]; deltas[i][1] = deltas[i-1][1]; } deltas[j+1][0] = value; deltas[j+1][1] = place; } /* ends if */ } /* ends loop over j */ /* last case */ if(value > deltas[PEAKS-2][0] && value < deltas[PEAKS-i][o]){ deltas[PEAKS-1][0] = value; deltas[PEAKS-1][1] = place; } /* ends if */ } /* ends insert_into_deltas */ /* End of File */
Listing 7 Code implementing the adaptive technique
/************************************************ * * adaptive_threshold_segmentation(... * * This function segments an image using * thresholding. It uses two passes * to find the hi and low values of the * threshold. The first pass uses the peaks * of the histogram to find the hi and low * threshold values. It thresholds the image * using these hi lows and calculates the means * of the object and background. Then we use * these means as new peaks to calculate new * hi and low values. Finally, we threshold * the image again using these second hi low * hi low values. * * If the segment parameter is 0, you only * threshold the array - you do not segment. * **************************************************/ adaptive_threshold_segmentation(in_name, out_name, the_image, out_image, il, ie, ll, le, value, segment) char in_name[], out_name[]; int il, ie, ll, le, segment; short the_image[ROWS] [COLS], out_image[ROWS] [COLS], value; { int length, peak1, peak2, width; short background, hi, low, object; struct tiff_header_struct image_header; unsigned long-histogram[GRAY_LEVELS+1]; if(does_not exist(out_name)){ printf("\n\nATS> output file does not exist %s", out_name); read_tiff_header(in_name, &image_header); round_off_image_size(&image_header, &length, &width); image_header.image_length = length*ROWS; image_header.image_width = width*COLS; create_allocate_tiff_file(out_name, &image_header, out_image); } /* ends if does not exist */ read_tiff_image(in_name, the_image, il, ie, ll, le); zero_histogram(histogram); calculate_histogram(the_image, histogram); smooth_histogram(histogram); find_peaks(histogram, &peak1, &peak2); peaks_high_low(histogram, peak1, peak2, &hi, &low); threshold_and_find_means(the_image, out_image, hi, low, value, &object, &background); peaks_high_low(histogram, object, background, &hi, &low); threshold_image_array(the_image, out_image, hi, low, value); if(segment == 1) grow(out_image, value); write_array_into_tiff_image(out_name, out_image, il, ie, ll, le); } /* ends adaptive_threshold_segmentation */ /************************************************ * * threshold_and_find_means(... * * This function thresholds an input image array * and produces a binary output image array. * If the pixel in the input array is between * the hi and low values, then it is set to value. * Otherwise, it is set to 0. * ************************************************/ threshold_and_find_means(in_image, out_image, hi, low, value, object_mean, background_mean) short *background_mean, hi, low, in_image[ROWS][COLS], *object_mean, out_image[ROWS][COLS], value; { int counter = 0, i, j; unsigned long object = 0, background = 0; for(i=0; i<ROWS; i++){ for(j=0; j<COLS; j++){ if(in_image[i][j] >= low && in_image[i][j] <= hi){ out_image[i][j] = value; counter++; object = object + in_image[i][j]; } else{ out_image[i][j] : 0; background = background + in_image[i][j]; } } /* ends loop over j */ } /* ends loop over i */ object = object/counter; background = background/((ROWS*COLS)-counter); *object_mean = (short)(object); *background mean = (short)(background); printf("\n\tTAFM> set %d points", counter); printf("\n\tTAFM> object=%d background=%d", *object_mean, *background_mean); } /* ends threshold_and_find_means */ /* End of File */
Listing 8 main routine for the C Image Processing System revised to include histogram image segmentation
case 16: /* image thresholding */ printf("\nCIPS> Enter input image name\n"); get_image_name(name); printf("\nCIPS> Enter output image name\n"); get_image_name(name2); get_parameters(&il, &ie, &ll, &le); get_threshold_options(ts_method, &hi, &low, &value); if (ts_method[0] == 'm' || ts_method[0] == 'M') manual_threshold_segmentation(name, name2, the_image, out_image, il, ie, ll, le, hi, low, value, 0); if(ts_method[0] == 'p' || ts_method[0] == 'P') peak_threshold_segmentation(name, name2, the_image, out_image, il, ie, ll, le, value, 0); if(ts_method[0] == 'v' || ts_method[0] == 'V') valley_threshold_segmentation(name, name2, the_image, out_image, il, ie, ll, le, value, 0); if(ts_method[0] == 'a' || ts_method[0] == 'a') adaptive_threshold_segmentation(name, name2, the_image, out_image, il, ie, ll, le, value, 0); break; case 17: /* image segmentation */ printf("\nCIPS> Enter input image name\n"); get_image_name(name); printf("\nCIPS> Enter output image name\n"); get_image_name(name2); get_parameters(&il, &ie, &ll, &le); get_segmentation_options(ts_method, &hi, &low, &value); if(ts_method[0] == 'm' || (ts_method[0] == 'M') manual_threshold_segmentation(name, name2, the_image, out_image, il, ie, ll, le, hi, low, value, 1); if(ts_method[0] == 'p' || ts_method[0] == 'P') peak_threshold_segmentation(name, name2, the_image, out_image, il, ie, ll, le, value, 1); if(ts_method[0] == 'v' || ts_method[0] == 'V') valley_threshold_segmentation(name, name2, the_image, out_image, il, ie, ll, le, value, 1); if(ts_method[0] == 'a' || ts_method[0] == 'a') adaptive_threshold_segmentation(name, name2, the_image. out_image, il, ie, ll, le, value, 1); break; . . . /**************************************************** * show_menu(.. * This function displays the CIPS main menu. *****************************************************/ show_menu() { printf("\n\nWelcome to CIPS"); printf("\nThe C Image Processing System"); printf("\nThese are you choices:"); printf("\n\t1. Display image header"); printf("\n\t2. Show image numbers"); printf("\n\t3. Print image numbers"); printf("\n\t4. Display image (VGA & EGA only)"); printf("\n\t5. Display or print image using halftoning"); printf("\n\t6. Print graphics image using dithering"); printf("\n\t7. Print or display histogram numbers"); printf("\n\t8. Perform edge detection"); printf("\n\t9. Perform edge enhancement"); printf("\n\t10. Perform image filtering"); printf("\n\t11. Perform image addition and subtraction"); printf("\n\t12. Perform image cutting and pasting"); printf("\n\t13. Perform image rotation and flipping"); printf("\n\t14. Perform image scaling"); printf("\n\t15. Create a blank image"); printf("\n\t16. Perform image thresholding"); printf("\n\t17. Perform image segmentation"); printf("\n\t20. Exit system"); printf("\n\nEnter choice _\b"); } /* ends show_menu */ . . . /****************************************** * get_segmentation_options(... * This function interacts with the user * to obtain the options for image * segmentation. *******************************************/ get_segmentation_options(method, hi, low, value) char method[]; short *hi, *low, *value; { int i, not_finished = 1, response; while(not_finished){ printf("\n\nThe image segmentation options are:\n"); printf("\n\t1. Method is %s", method); printf("\n\t (options are manual peaks"); printf( " valleys adapative)"); printf("\n\t2. Value is %d", *value); printf("\n\t3. Hi is %d", *hi); printf("\n\t4. Low is %d", *low); printf("\n\t Hi and Low needed only for"); printf( "manual method"); printf("\n\nEnter choice (0 = no change):_\b"); get_integer(&response); if(response == 0) not_finished == 0; if(response == 1){ printf("\nEnter method (options are:"); printf(" manual peaks valleys adaptive)\n\t"); read_string(method); } if(response == 2){ printf("\nEnter value: __\b\b\b"); get_short(value); } if(response == 3){ printf("\nEnter hi: __\b\b\b"); get_short(hi); } if(response == 4){ printf("\nEnter low: __\b\b\b"); get_short(low); } } /* ends while not_finished */ } /* ends get_segmentation_options */ /********************************************* * get_threshold_options{... * This function interacts with the user * to obtain the options for image * threshold. **********************************************/ get_threshold_options(method, hi, low, value) char method[]; short *hi, *low, *value; { int i, not_finished = 1, response; while(not_finished){ printf("\n\nThe image threshold options are:\n"); printf("\n\tl. Method is %s", method); printf("\n\t (options are manual peaks"); printf( "valleys adapative)"); printf("\n\t2. Value is %d", *value); printf("\n\t3. Hi is %d", *hi); printf("\n\t4. Low is %d", *low); printf("\n\t Hi and Low needed only for"); printf( " manual method"); printf("\n\nEnter choice (0 = no change):_\b"); get_integer(&response); if(response == 0) not_finished = 0; if(response == 1){ printf("\nEnter method (options are:"); printf(" manual peaks valleys adaptive)\n\t"); read_string(method); } if(response == 2){ printf("\nEnter value: __\b\b\b"); get_short(value); } if(response == 3){ printf("\nEnter hi: __\b\b\b"); get_short(hi); } if(response == 4){ printf("\nEnter low: __\b\b\b"); get_short(low); } } /* ends while not_finished */ } /* ends get_threshold_options */ /* End of File */
Listing 9 Application for thresholding and segmenting an image file
/*************************************** * * file d:\cips\mainseg.c * * Functions: This file contains * main * * Purpose: * This file contains the main calling * routine in an edge detection program. * * External Calls: * gin.c - get_image_name * numcvrt.c - get_integer * int_convert * tiff.c - read_tiff_header * enhance_edges * hist.c - zero_histogram * smooth_histogram * show_histogram * calculate_histogram * segment.c - threshold_image_array * grow * find_peaks * peaks_high_low * valley_high_low * threshold_and_find_means * * Modifications: * 27 September 1992 - created * ****************************************/ #include "cips.h" short the_image[ROWS][COLS]; short out_image[ROWS][COLS]; unsigned long histogram[GRAY_LEVELS+1]; main(argc, argv) int argc; char *argv[]; { char name[80], name2[80], response[80]; int count, i, ie, i1, j, k, le, length, ll, peakl, peak2, size, t, type, v, width; short background, hi, low, object, value; struct tiff_header_struct image_header; _setvideomode(_TEXTC80); /* MSC 6.0 statements */ _setbkcolor(1); _settextcolor(7); _clearscreen(_GCLEARSCREEN); if(argc < 7){ printf("\n\nmainseg in-file out-file hi low value operation"); printf("\n\t\toperation = threshold grow peaks valleys adaptive"); printf("\n"); exit(0); } strcpy(name, argv[1]); strcpy(name2, argv[2]); hi = atoi(argv[3]); low = atoi(argv[4]); value = atoi(argv[5]); il = 1; ie = 1; ll = ROWS+1; le = COLS+1; read_tiff_header(name, &image_header); length = (90 + image_header.image_length)/ROWS; width = (90 +image_header.image_width)/COLS; count = 1; printf("\nlength=%d width=%d", length, width); if(does_not_exist(name2)){ read_tiff_header(name, &image_header); round_off_image_size(&image_header, &length, &width); image_header.image_length = length*ROWS; image_header;image_width = width*COLS; create_allocate_tiff_file(name2, &image_header, out_image); } /* ends if does_not_exist */ zero_histogram(histogram); /********************************** * * Manual Threshold operation * ***********************************/ if(argv[6][0] == 't'){ for(i=0; i<length; i++){ for(j=0; j<width; j++){ printf("\nrunning %d of %d", count, length*width); count++; read_tiff_image(name, the_image, il+i*ROWS, ie+j*COLS, ll+i*ROWS, le+j*COLS); printf("\nMS> Calling threshold"); threshold_image_array(the_image, out_image, hi, low, value); write_array_into_tiff_image(name2, out_image, il +i*ROWS, ie+j*COLS, 11+i*ROWS, le+j*COLS); } /* ends loop over i */ } /* ends loop over j */ } /* ends if t */ /********************************** * * Grow region operation * **********************************/ if(argv[6][0] == 'g'){ for(i=0; i<length; i++){ for(j=0; j<width; j++){ printf("\nrunning %d of %d", count, length*width); count++; read_tiff_image(name, the_image, il+i*ROWS, ie+j*COLS, 11+i*ROWS, le+j*COLS); printf("\nMS> Calling grow"); grow(the_image, value); write_array_into_tiff_image(name2, the_image, il+i*ROWS, ie+j*COLS, 11+i*ROWS, le+j*COLS); } /* ends loop over i */ } /* ends loop over j */ } /* ends if g */ /********************************** * * Peak threshold operation * ***********************************/ if(argv[6][0] == 'P'){ /* calculate histogram for the entire image file */ zero_histogram(histogram); for(i=0; i<length; i++){ for(j=0; j<width; j++){ printf("\nrunning %d of %d", count, length*width); count++; read_tiff_image(name, the_image, il+i*ROWS, ie+j*COLS, ll+i*ROWS, le+j*COLS); printf("\nMS> Calling hist functions"); calculate_histogram(the_image, histogram); } /* ends loop over i */ } /* ends loop over j */ smooth_histogram(histogram); show_histogram(histogram); find_peaks(histogram, &peak1, &peak2); printf("\npeakl=%d peak2=%d", peak1, peak2); peaks_high_low(histogram, peak1, peak2, &hi, &low); printf("\nhi=%d low=%d", hi, low); /* now read the image file again and threshold and grow objects. */ count = 1; for(i=0; i<length; i++){ for(j=0; j<width; j++){ printf("\nrunning %d of %d", count, length*width); count++; read_tiff_image(name, the_image, il+i*ROWS, ie+j*COLS, ll+i*ROWS, le+j*COLS); threshold_image_array(the_image, out_image, hi, low, value); write_array_into_tiff_image(name2, out_image, il+i*ROWS, ie+j*COLS, ll+i*ROWS, le+j*COLS); } /* ends loop over i */ } /* ends loop over j */ } /* ends if p */ /******************************** * * Valley threshold operation * ********************************/ if(argv[6][0] = = 'v'){ /* calculate histogram for the entire image file */ zero_histogram(histogram); for(i=0; i<length; i++){ for(j=0; j<width; j++){ printf("\nrunning %d of %d", count, length*width); count++; read_tiff_image(name, the_image, il+i*ROWS, ie+j*COLS, ll+i*ROWS, le+j*COLS); printf("\nMS> Calling hist functions"); calculate_histogram(the_image, histogram); } /* ends loop over i */ } /* ends loop over j */ smooth_histogram(histogram); show_histogram(histogram); find_peaks(histogram, &peak1, &peak2); printf("\npeakl=%d peak2=%d", peak1, peak2); valley_high_low(histogram, peak1, peak2, &hi, &low); printf("\nhi=%d low=%d", hi, low); /* now read the image file again and threshold and grow objects. */ count = 1; for(i=0; i<length; i++){ for(j=0; j<width; j++){ printf("\nrunning %d of %d", count, length*width); count++; read_tiff_image(name, the_image, il+i*ROWS, ie+j*COLS, ll+i*ROWS, le+j*COLS); threshold_image_array(the_image, out_image, hi, low, value); write_array_into_tiff_image(name2, out_image, il+i*ROWS, ie+j*COLS, ll+i*ROWS, le+j*COLS); } /* ends loop over i */ } /* ends loop over j */ } /* ends if v */ /******************************** * * Adaptive threshold operation * ********************************/ if(argv[6][0] == 'a'){ /* calculate histogram for the entire image file */ zero_histogram(histogram); for(i=0; i<length; i++){ for(j=0; j<width; j++){ printf("\nrunning %d of %d", count, length*width); count++; read_tiff_image(name, the_image, il+i*ROWS, ie+j*COLS, ll+i*ROWS, le+j*COLS); printf("\nMS> Calling hist functions"); calculate_histogram(the_image, histogram); } /* ends loop over i */ } /* ends loop over j */ /* find the peaks for the entire image file. */ smooth_histogram(histogram); show_histogram(histogram); find_peaks(histogram, &peakl, &peak2); printf("\npeakl=%d peak2=%d", peak1, peak2); peaks_high_low(histogram, peak1, peak2, &hi, &low); printf("\nhi=%d low=%d", hi, low); /* Second Pass */ count = 1; for(i=0; i<length; i++){ for(j=0; j<width; j++){ printf("\nrunning %d of %d", count, length*width); count++; read_tiff_image(name, the_image, il+i*ROWS, ie+j*COLS, ll+i*ROWS, le+j*COLS); threshold_and find_means(the_image, out_image, hi, low, value, &object, &background); peaks_high_low(histogram, object, background, &hi, &low); printf("\nafter means calculated - hi=%d low=%d", hi, low); threshold_image_array(the_image, out_image, hi, low, value); write_array_into_tiff_image(name2, out_image, il+i*ROWS, ie+j*COLS, ll+i*ROWS, le+j*COLS); } /* ends loop over i */ } /* ends loop over j */ } /* ends if a */ } /* ends main */ /* End of File */