├── README.md ├── accept_solution.m ├── bi2de.m ├── convert_thresholds.m ├── crossover.m ├── crossover_one.m ├── first_best.m ├── fitness.m ├── fitness_one.m ├── ga_segmentation.m ├── images ├── cameraman.png └── img.png ├── initialization.m ├── mutate_one.m ├── mutation.m └── threshold_bin2dec.m /README.md: -------------------------------------------------------------------------------- 1 | ## Image Segmentation Using Genetic Algorithm 2 | 3 | Project is inspired by [paper](http://www.worldcomp-proceedings.com/proc/p2011/IPC8346.pdf) ([summary](http://breadthsearch.blogspot.cz/2014/11/multi-thresholding-image-segmentation.html)). 4 | Image segmentation can be pursued by many different ways. 5 | One of them is called multi-thresholding. 6 | Since we want to segment image to more than two segments (more than one threshold) we need to determine at least two thresholds. 7 | If we want to segment gray level image with 256 levels to three segments we are likely supposed to examine 256*255 = 65280 different threshold combinations. 8 | This brute force approach would end up with optimal solution, however computing time would be definitely high. 9 | Genetic algorithm searches space containing all possible solutions and obtain the best solution among all examined in much less time than brute force algorithm. 10 | 11 | 12 | 13 | 14 | 15 | *The result of segmentation by genetic algorithm with population size 20 and number of iterations 30.* 16 | 17 | ## Implementation 18 | Genetic algorithm was implemented in GNU Octave. 19 | This language was selected due to its fast prototyping. 20 | The speed of algorithm is not the matter of interest. 21 | 22 | #### Initialization 23 | This part of code is found at *ga_segmentation.m*. 24 | There are few settings which can affect the result of algorithm. 25 | * **n_population:** 26 | size of population; contains different solutions 27 | * **n_iterations:** 28 | number of iterations; algorithm terminates after all iterations are done 29 | * **n_thresholds:** 30 | number of desired thresholds; n_thresholds = (number of segments - 1) 31 | 32 | The next three settings are related to each other. 33 | They are ratios of selection (**n_selection**), crossover (**n_crossover**) and mutation (**n_mutation**). 34 | The sum of all these parameters has to be equal to 1. 35 | This condition guarantees the size of population does not change between iterations. 36 | 37 | Single chromose is implemented as vector of binary numbers. 38 | The length of vector is *L* * *n*, where *L* denotes *log*(number of gray levels) and *n* is the number of desired thresholds. 39 | Population is represented a matrix, where each row is single chromosome and number of rows corresponds to size of population. 40 | An initial population was randomly generated. 41 | 42 | In this part we also load a image for segmentation. 43 | Algorithm works only with grayscale images, so we always convert each image at the beginning before we start to work with it. 44 | 45 | #### Evaluation of fitness 46 | According to paper, which was mentioned above, evaluation of fitness should be performed with ratio between between inter-object variance and intra-object variance. 47 | We have decided for sligthly easier evaluation based on sum of intra-object variance. 48 | The solution with the lowest sum is the most accurate. 49 | 50 | *fitness.m*, *fitness_one.m* 51 | 52 | #### Selection 53 | Current solution selects to new generation only the best solutions. 54 | The number of forwarded solution depends on ratio **n_selection**. 55 | 56 | *first_best.m* 57 | 58 | #### Crossover 59 | Chromosomes, which are supposed to crossover, are chosen from two vectors containing radomly permutated indexes (numbers of row) of chromosomes. 60 | The number chromosomes, which crossover, depends on ratio **n_crossover**. 61 | One-point crossover is employed in current solution. 62 | The point of crossover is randomly generated with uniform distribution. 63 | 64 | *crossover.m*, *crossover_one.m* 65 | 66 | #### Mutation 67 | Indexes (number of rows) of chromosomes are randomly permutated and the very first of them are more likely to be chosen. 68 | The number of chromosomes, which happens to be chosen for mutation, depends on ratio **n_mutation**. 69 | Current solution allows to mutate only one gene of chromosome. 70 | 71 | *mutation.m*, *mutate_one.m* 72 | 73 | ## Experiments 74 | Two experiments were performed: 75 | * The first one with picture composed from solid colors 76 | * and the second one uses benchmark from website [mosaic.utia.cas.cz](http://mosaic.utia.cas.cz/). 77 | 78 | During the experiment we observed that 20 as size of population and 50 as number of iterations are satisfying for most of cases. 79 | 80 | #### Solid colors 81 | Though segmentation of this picture looks promising, we were not able to successfully segment all parts. 82 | The best result can be shown at the end of this subsection. 83 | We also noticed that algorithm is prone to insignificant segments (edges of shapes) with small area. 84 | 85 | 86 | 87 | *Source image contains 8 different colors.* 88 | 89 | 90 | 91 | 92 | 93 | *2 thresholds, 3 segments.* 94 | 95 | 96 | 97 | *10 thresholds, 11 segments.* 98 | 99 | #### Benchmark 100 | Benchmark consist from 20 different images with artificially created texture segments. 101 | The number of segments is between 3 and 12, which can be sometimes even challenging for human. 102 | 103 | Each triplet of images below represents source image, ground truth of segmentation and segmentation done by genetic algorithm. 104 | As we could expect the results have not reached high. 105 | Correct segmentation is 2.17 % overall. 106 | 107 | 108 | 109 | 110 | 111 | *2 thresholds, 3 segments.* 112 | 113 | 114 | 115 | 116 | 117 | *11 thresholds, 12 segments.* 118 | 119 | ## Conclusion 120 | Genetic algorithm is able to find suboptiomal solution of multi-threshold segmentation. 121 | This solution is not suitable for all kinds of segmentation problems; e.g. binary segmentation: there are more reliable methods like Balanced histogram thresholding or Otsu's method. 122 | The low performance of texture segmentation was confirmed by benchmark. 123 | 124 | The shortcoming of implemented solution is propensity to segment small areas like edges. 125 | These areas are not essential in segmentation and therefore should not taken into account. 126 | 127 | The algorithm could be refined by two-point crossover or roulette wheel for selecting chromosomes to the next population. 128 | -------------------------------------------------------------------------------- /accept_solution.m: -------------------------------------------------------------------------------- 1 | % MULTI-THRESHOLDING IMAGE SEGMENTATION USING GENETIC ALGORITHMS 2 | % Martin Kersner, 3 | % 11/27/2014 4 | % 5 | % Employs the best solution and displays segments of examined image. 6 | 7 | function accept_solution(image, population, n_thresholds) 8 | 9 | segmentation = zeros(size(image)); 10 | segmentation_value = 1/n_thresholds; 11 | genome_size = size(population, 2)/n_thresholds; 12 | 13 | % Retrieve the best solution 14 | [b, b_i] = sort(fitness(image, population, n_thresholds)); 15 | best_genome = population(b_i(1),:); 16 | 17 | threshold = sort(threshold_bin2dec(best_genome, n_thresholds)); 18 | 19 | value = 0; 20 | end_i = size(threshold, 2) + 1; 21 | for i = 1:end_i 22 | if (i == 1) 23 | % The first threshold 24 | left = 0; 25 | right = threshold(i); 26 | elseif (i == end_i) 27 | % The last threshold 28 | left = threshold(i-1); 29 | right = max(image(:)); 30 | else 31 | % Regular threshold 32 | left = threshold(i-1); 33 | right = threshold(i); 34 | endif 35 | 36 | % <0; x) = left; 38 | right_mask = image < right; 39 | mask = left_mask .* right_mask; 40 | 41 | segmentation += value*mask; 42 | 43 | % Display segments 44 | %if (i >= 2) 45 | % figure 46 | % mask_value = value*mask; 47 | % imshow(mask_value); 48 | % imwrite(mask_value, strcat(num2str(i), ".png")); 49 | %endif 50 | 51 | value += segmentation_value; 52 | endfor 53 | 54 | imshow(segmentation); 55 | 56 | endfunction 57 | -------------------------------------------------------------------------------- /bi2de.m: -------------------------------------------------------------------------------- 1 | ## Copyright (C) 2001 Laurent Mazet 2 | ## 3 | ## This program is free software; you can redistribute it and/or modify 4 | ## it under the terms of the GNU General Public License as published by 5 | ## the Free Software Foundation; either version 2 of the License, or 6 | ## (at your option) any later version. 7 | ## 8 | ## This program is distributed in the hope that it will be useful, 9 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ## GNU General Public License for more details. 12 | ## 13 | ## You should have received a copy of the GNU General Public License 14 | ## along with this program; if not, write to the Free Software 15 | ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | ## -*- texinfo -*- 18 | ## @deftypefn {Function File} {@var{d} = } bi2de (@var{b}) 19 | ## @deftypefnx {Function File} {@var{d} = } bi2de (@var{b},@var{p}) 20 | ## @deftypefnx {Function File} {@var{d} = } bi2de (@var{b},@var{p},@var{f}) 21 | ## 22 | ## Convert bit matrix to a vector of integers 23 | ## 24 | ## Each row of the matrix @var{b} is treated as a single integer represented 25 | ## in binary form. The elements of @var{b}, must therefore be '0' or '1' 26 | ## 27 | ## If @var{p} is defined then it is treated as the base of the decomposition 28 | ## and the elements of @var{b} must then lie between '0' and 'p-1'. 29 | ## 30 | ## The variable @var{f} defines whether the first or last element of @var{b} 31 | ## is considered to be the most-significant. Valid values of @var{f} are 32 | ## 'right-msb' or 'left-msb'. By default @var{f} is 'right-msb'. 33 | ## @end deftypefn 34 | ## @seealso{de2bi} 35 | 36 | ## 2001-02-02 37 | ## initial release 38 | ## 2003-02-02 39 | ## add orientation of b and help in texinfo 40 | 41 | function d = bi2de (b, p, f) 42 | 43 | switch (nargin) 44 | case 1, 45 | p = 2; 46 | f = 'right-msb'; 47 | case 2, 48 | if (isstr(p)) 49 | f = p; 50 | p = 2; 51 | else 52 | f = 'right-msb'; 53 | endif 54 | case 3, 55 | if (isstr(p)) 56 | tmp = f; 57 | f = p; 58 | p = tmp; 59 | endif 60 | otherwise 61 | error ("usage: d = bi2de (b, [p])"); 62 | endswitch 63 | 64 | if ( any (b (:) < 0) || any (b (:) > p - 1) ) 65 | error ("bi2de: d must only contain value in [0, p-1]"); 66 | endif 67 | 68 | if (strcmp(f,'left-msb')) 69 | b = b(:,size(b,2):-1:1); 70 | elseif (!strcmp(f,'right-msb')) 71 | error("bi2de: unrecognized flag"); 72 | endif 73 | 74 | if (length (b) == 0) 75 | d = []; 76 | else 77 | d = b * ( p .^ [ 0 : (columns(b)-1) ]' ); 78 | endif 79 | 80 | endfunction; 81 | -------------------------------------------------------------------------------- /convert_thresholds.m: -------------------------------------------------------------------------------- 1 | % MULTI-THRESHOLDING IMAGE SEGMENTATION USING GENETIC ALGORITHMS 2 | % Martin Kersner, 3 | % 11/26/2014 4 | % 5 | % Converts binary thresholds of all population to decimal representation. 6 | 7 | function thresholds = convert_thresholds(population, n_thresholds) 8 | 9 | thresholds = []; 10 | population_size = size(population, 1); 11 | 12 | for i = 1:population_size 13 | thresholds = [thresholds; threshold_bin2dec(population(i,:), n_thresholds)]; 14 | endfor 15 | 16 | endfunction 17 | -------------------------------------------------------------------------------- /crossover.m: -------------------------------------------------------------------------------- 1 | % MULTI-THRESHOLDING IMAGE SEGMENTATION USING GENETIC ALGORITHMS 2 | % Martin Kersner, 3 | % 11/27/2014 4 | % 5 | % Crossovers desired part of population. 6 | 7 | function new_population = crossover(population, p_crossover, new_population) 8 | 9 | population_size = size(population, 1); 10 | 11 | % Random permutation of genomes order 12 | parent_first = randperm(population_size); 13 | parent_second = randperm(population_size); 14 | 15 | % Number of couples used for crossover 16 | n_crossovers = round(p_crossover*population_size)/2; 17 | 18 | for i = 1:n_crossovers 19 | % Crossovers parents 20 | [desc_first desc_second] = crossover_one(population(parent_first(i), :), ... 21 | population(parent_second(i), :)); 22 | 23 | % Add crossover descendants 24 | new_population = [new_population; desc_first; desc_second]; 25 | endfor 26 | 27 | endfunction 28 | -------------------------------------------------------------------------------- /crossover_one.m: -------------------------------------------------------------------------------- 1 | % MULTI-THRESHOLDING IMAGE SEGMENTATION USING GENETIC ALGORITHMS 2 | % Martin Kersner, 3 | % 11/27/2014 4 | % 5 | % Crossover of two parents creating new descendants by one-point crossover. 6 | 7 | function [desc_first desc_second] = crossover_one(parent_first, parent_second) 8 | 9 | parent_size = size(parent_first, 2); 10 | 11 | % Randomly generated number between 1 and the length of parent's genome. 12 | point = round(unifrnd(1, parent_size-1)); 13 | 14 | % Crossover 15 | desc_first = [parent_first(1:point) parent_second(point+1:parent_size)]; 16 | desc_second = [parent_second(1:point) parent_first(point+1:parent_size)]; 17 | 18 | endfunction 19 | -------------------------------------------------------------------------------- /first_best.m: -------------------------------------------------------------------------------- 1 | % MULTI-THRESHOLDING IMAGE SEGMENTATION USING GENETIC ALGORITHMS 2 | % Martin Kersner, 3 | % 11/27/2014 4 | % 5 | % Selecting the first best solutions from current population and 6 | % transfering to new population. 7 | 8 | function new_population = first_best(ranking, population, p_selection, new_population) 9 | 10 | population_size = size(population, 1); 11 | [best, best_i] = sort(ranking); 12 | 13 | for i = 1:round(p_selection*population_size) 14 | new_population = [new_population; population(best_i(i), :)]; 15 | endfor 16 | 17 | endfunction 18 | -------------------------------------------------------------------------------- /fitness.m: -------------------------------------------------------------------------------- 1 | % MULTI-THRESHOLDING IMAGE SEGMENTATION USING GENETIC ALGORITHMS 2 | % Martin Kersner, 3 | % 11/26/2014 4 | % 5 | % Computes fitness ranking for all population. 6 | 7 | function ranking = fitness(image, population, n_thresholds) 8 | 9 | ranking = []; 10 | 11 | % Convert thresholds to decimal representation 12 | thresholds = convert_thresholds(population, n_thresholds); 13 | 14 | % Vectorize image 15 | image_vec = image(:); 16 | 17 | % Computes fitness ranking for all thresholds in population 18 | for i = 1:size(thresholds, 1) 19 | ranking = [ranking; fitness_one(image_vec, thresholds(i,:))]; 20 | endfor 21 | 22 | endfunction 23 | -------------------------------------------------------------------------------- /fitness_one.m: -------------------------------------------------------------------------------- 1 | % MULTI-THRESHOLDING IMAGE SEGMENTATION USING GENETIC ALGORITHMS 2 | % Martin Kersner, 3 | % 11/26/2014 4 | % 5 | % Computes fitness ranking for given chromosome. 6 | 7 | function ranking = fitness_one(image_vec, thresholds_vec) 8 | 9 | ranking = 1; 10 | inter_var = 0; 11 | intra_var = 0; % TODO implement 12 | 13 | % Sort thresholds 14 | thresholds_vec = sort(thresholds_vec); 15 | 16 | end_i = size(thresholds_vec, 2) + 1; 17 | for i = 1:end_i 18 | if ((i == 1 && end_i == 2) || i == 1) 19 | % One threshold or the first threshold 20 | left = 0; 21 | right = thresholds_vec(i); 22 | elseif (i == end_i) 23 | % The last threshold 24 | left = thresholds_vec(i-1); 25 | right = max(image_vec); 26 | else 27 | % More thresholds 28 | left = thresholds_vec(i-1); 29 | right = thresholds_vec(i); 30 | endif 31 | 32 | % <0; x) = left; 34 | right_mask = image_vec < right; 35 | mask = left_mask .* right_mask; 36 | object = image_vec(find(mask)); 37 | 38 | % TODO better way to relate all variances within objects? 39 | if (length(object) == 0) 40 | variance = 1; 41 | else 42 | variance = var(object); 43 | endif 44 | 45 | ranking = ranking + variance; 46 | endfor 47 | 48 | endfunction 49 | -------------------------------------------------------------------------------- /ga_segmentation.m: -------------------------------------------------------------------------------- 1 | % MULTI-THRESHOLDING IMAGE SEGMENTATION USING GENETIC ALGORITHMS 2 | % Martin Kersner, 3 | % 11/24/2014 4 | 5 | pkg load image 6 | 7 | % Default variables 8 | n_population = 20; 9 | n_iterations = 50; 10 | n_bins = 256; 11 | n_thresholds = 5; 12 | 13 | % Ratios of all GA operations 14 | p_selection = 0.1; 15 | p_crossover = 0.8; 16 | p_mutation = 0.1; 17 | assert(sum([p_selection, p_crossover, p_mutation]) == 1, 'Total sum of proportions have to be 1!'); 18 | 19 | % Read image 20 | image = imread("images/img.png"); 21 | 22 | % Convert image to gray levels 23 | if (size(image, 3) == 3) 24 | image_gray = rgb2gray(image); 25 | else 26 | image_gray = image; 27 | endif 28 | 29 | % Initialization 30 | population = initialization(n_population, n_bins, n_thresholds); 31 | 32 | for i = 1:n_iterations 33 | new_population = []; 34 | 35 | % Evaluation of fitness 36 | ranking = fitness(image, population, n_thresholds); 37 | 38 | %% Reproduction 39 | % Selection 40 | % TODO create more strategies (like roulette wheel) 41 | new_population = first_best(ranking, population, p_selection, new_population); 42 | 43 | % Crossover 44 | new_population = crossover(population, p_crossover, new_population); 45 | 46 | % Mutation 47 | new_population = mutation(population, p_mutation, new_population); 48 | 49 | population = new_population; 50 | endfor 51 | 52 | % Accepting the solution 53 | accept_solution(image_gray, population, n_thresholds); 54 | -------------------------------------------------------------------------------- /images/cameraman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinkersner/Image-Segmentation-Using-Genetic-Algorithm/c410a7201cf13fdb4907d1aff5596890cf76fc0a/images/cameraman.png -------------------------------------------------------------------------------- /images/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinkersner/Image-Segmentation-Using-Genetic-Algorithm/c410a7201cf13fdb4907d1aff5596890cf76fc0a/images/img.png -------------------------------------------------------------------------------- /initialization.m: -------------------------------------------------------------------------------- 1 | % MULTI-THRESHOLDING IMAGE SEGMENTATION USING GENETIC ALGORITHMS 2 | % Martin Kersner, 3 | % 11/26/2014 4 | % 5 | % Randomly generates the first population. 6 | 7 | function population = initialization(n_population, n_bins, n_thresholds) 8 | 9 | population = round(unifrnd(0, 1, [n_population ceil(log2(n_bins))*n_thresholds])); 10 | 11 | endfunction 12 | -------------------------------------------------------------------------------- /mutate_one.m: -------------------------------------------------------------------------------- 1 | % MULTI-THRESHOLDING IMAGE SEGMENTATION USING GENETIC ALGORITHMS 2 | % Martin Kersner, 3 | % 11/27/2014 4 | % 5 | % Mutates given chromose at one randomly generated position. 6 | 7 | function new_chromosome = mutate_one(chromosome) 8 | 9 | new_chromosome = chromosome; 10 | 11 | chromosome_size = size(chromosome, 2); 12 | gene = round(unifrnd(1, chromosome_size)); 13 | 14 | % Mutate one gene 15 | if (chromosome(gene) == 1) 16 | new_chromosome(gene) = 0; 17 | else 18 | new_chromosome(gene) = 1; 19 | endif 20 | 21 | endfunction 22 | -------------------------------------------------------------------------------- /mutation.m: -------------------------------------------------------------------------------- 1 | % MULTI-THRESHOLDING IMAGE SEGMENTATION USING GENETIC ALGORITHMS 2 | % Martin Kersner, 3 | % 11/27/2014 4 | % 5 | 6 | function new_population = mutation(population, p_mutation, new_population) 7 | 8 | population_size = size(population, 1); 9 | 10 | % Random permutation of genomes order 11 | mutation_order = randperm(population_size); 12 | 13 | for i = 1:round(p_mutation*population_size); 14 | new_population = [new_population; mutate_one(population(mutation_order(i), :))]; 15 | endfor 16 | 17 | endfunction 18 | -------------------------------------------------------------------------------- /threshold_bin2dec.m: -------------------------------------------------------------------------------- 1 | % MULTI-THRESHOLDING IMAGE SEGMENTATION USING GENETIC ALGORITHMS 2 | % Martin Kersner, 3 | % 11/27/2014 4 | % 5 | % Converts thresholds of one genome from binary to decimal format. 6 | 7 | function dec_thresholds = threshold_bin2dec(bin_thresholds, n_thresholds) 8 | 9 | dec_thresholds = []; 10 | threshold_length = (size(bin_thresholds, 2)/n_thresholds); 11 | 12 | for i = 1:threshold_length:size(bin_thresholds, 2) 13 | dec_thresholds = [dec_thresholds, bi2de(bin_thresholds(i:i+threshold_length-1))]; 14 | endfor 15 | 16 | endfunction 17 | --------------------------------------------------------------------------------