class Utils::Image
Public Class Methods
new(ex_path)
click to toggle source
# File lib/ruby_pager/image.rb, line 7 def initialize(ex_path) @logger = Utils::ApplicationLogger.instance @image_path = ex_path @logger.info("Loading image") @img = Magick::ImageList.new(@image_path) @increment = 200 @img_aux = Magick::Image.new(@img.columns+@increment,@img.rows){ self.background_color = "white" } @intensity_calculated = false @integral_calculated = false end
Public Instance Methods
binarize!(method,*params)
click to toggle source
# File lib/ruby_pager/image.rb, line 230 def binarize!(method,*params) @logger.info("Performing #{method} binarization on image") calculate_intensity_matrix @intensity_matrix = send(method,@intensity_matrix,*params) @logger.info("IMAGE CHANGED!! Recalculating internal representation") @img=intensity_matrix_to_image(@intensity_matrix) calculate_integral_images @histogram = @intensity_matrix.to_normalized_histogram end
calculate_intensity_matrix()
click to toggle source
# File lib/ruby_pager/image.rb, line 18 def calculate_intensity_matrix unless @intensity_calculated @logger.info("Calculating intensity matrix") @intensity_matrix = obtain_image_intensity_matrix #obtains the gray channel of the image @logger.info("Calculating normalized intensity histogram") @histogram = @intensity_matrix.to_normalized_histogram @intensity_calculated=true end end
columns()
click to toggle source
# File lib/ruby_pager/image.rb, line 33 def columns @img.columns end
detect_connected_regions(neighbourhood,output_file,intensity_matrix=@intensity_matrix)
click to toggle source
# File lib/ruby_pager/image.rb, line 259 def detect_connected_regions(neighbourhood,output_file,intensity_matrix=@intensity_matrix) label_assignment_matrix = Matrix.build(intensity_matrix.row_size,intensity_matrix.column_size){0} labels_translation = Hash.new labels_translation[0]=0 #label of the empty regions @logger.info("Detecting connected regions") intensity_matrix.each_with_index do |val,x,y| next unless val.zero? neighbour_labels = neighbour_labels_set(label_assignment_matrix,x,y,neighbourhood) if neighbour_labels.empty? new_label=labels_translation.keys.last+1 labels_translation[new_label]=new_label label_assignment_matrix[x,y]=new_label else #we always assign the label with least number, so that level equivalence translation will be linear new_label=neighbour_labels.first label_assignment_matrix[x,y]=new_label neighbour_labels.each{|label| labels_translation[label]=new_label } if neighbour_labels.size > 1 end end @logger.info("Translating region labels as per equivalence detected") labels_translation.each{|key,val|labels_translation[key]=labels_translation[val] if key != val} draw_connected_regions(extract_regions(label_assignment_matrix,labels_translation),output_file) end
display()
click to toggle source
# File lib/ruby_pager/image.rb, line 38 def display @img.display {server_name = @image_path; delay=1} end
draw_binary_image(method,*params)
click to toggle source
# File lib/ruby_pager/image.rb, line 245 def draw_binary_image(method,*params) @logger.info("Performing #{method} binarization on image") bin_mat = send(method,@intensity_matrix,*params) draw_intensity_matrix(bin_mat) end
draw_connected_regions(region_list,output_file)
click to toggle source
# File lib/ruby_pager/image.rb, line 318 def draw_connected_regions(region_list,output_file) @logger.info("Drawing regions") colors=["red","seagreen","royalblue","purple","sienna","steelblue","khaki","lightcoral","olive"] region_list.each_value do |region| gc = Magick::Draw.new gc.stroke_width = 1 gc.fill=colors.rotate![0] region.each do |point| gc.point(point[0],point[1]) end gc.draw(@img) end @img.display to_file(output_file,@img) end
draw_intensity_matrix(intensity_matrix=@intensity_matrix)
click to toggle source
# File lib/ruby_pager/image.rb, line 240 def draw_intensity_matrix(intensity_matrix=@intensity_matrix) @logger.info("Drawing intensity matrix") intensity_matrix_to_image(intensity_matrix).display end
extract_regions(label_assignment_matrix,label_translation)
click to toggle source
# File lib/ruby_pager/image.rb, line 305 def extract_regions(label_assignment_matrix,label_translation) @logger.info("Extracting regions to drawable format") regions= Hash.new{|h,key|h[key]=Array.new} #Array that will initialize an array for the key if not specified yet label_assignment_matrix.each_with_index do |val,x,y| unless label_translation[val].zero? region_target=label_translation[val] regions[region_target].push([y,x]) end end return regions end
intensity_matrix_to_image(intensity_matrix=@intensity_matrix)
click to toggle source
# File lib/ruby_pager/image.rb, line 111 def intensity_matrix_to_image(intensity_matrix=@intensity_matrix) @logger.info("Transforiming intensity matrix to image") return Magick::Image.constitute(intensity_matrix.column_size, intensity_matrix.row_size,"I", intensity_matrix.to_elements) end
mean_of_region(from_x=@integral_image.row_size-1,from_y=@integral_image.column_size-1,region_height=@integral_image.row_size-1,region_width=@integral_image.column_size-1)
click to toggle source
# File lib/ruby_pager/image.rb, line 42 def mean_of_region(from_x=@integral_image.row_size-1,from_y=@integral_image.column_size-1,region_height=@integral_image.row_size-1,region_width=@integral_image.column_size-1) #if no parameters are given the input variables get assigned the default values indicated in the above specification and hence return the mean of the whole image #raise "Region size provided exceeds the image boundaries" if from_y - region_width < 0 or from_x - region_height < 0 =begin [1]-------[2] height + 1 | | | | basic formula is [4] - [2] - [3] + [1] but if [2] or [3] coincide with boundary size then | | we do not have to delete the region beyond width or height ( as there is no region beyond) | | hence we do not have to add [1] to recover that corner section from being substracted twice [3]-------[4] width +1 =end res = @integral_image[from_x,from_y] region_cuts = 0 if from_y - region_width > 0 res -= @integral_image[from_x,from_y-(region_width+1)] region_cuts+=1 end if from_x - region_height > 0 res -= @integral_image[from_x-(region_height+1),from_y] region_cuts+=1 end if region_cuts == 2 res += @integral_image[from_x-(region_height+1),from_y-(region_width+1)] end return res.to_f/(region_height*region_width).to_f end
neighbour_labels_set(label_assignment_matrix,x,y,neighbourhood)
click to toggle source
# File lib/ruby_pager/image.rb, line 288 def neighbour_labels_set(label_assignment_matrix,x,y,neighbourhood) result = SortedSet.new result.add(label_assignment_matrix[x-1,y]) result.add(label_assignment_matrix[x,y-1]) if neighbourhood == 8 result.add(label_assignment_matrix[x-1,y-1]) result.add(label_assignment_matrix[x-1,y+1]) end result.delete(nil) result.delete(0) return result end
obtain_image_intensity_matrix(img=@img)
click to toggle source
# File lib/ruby_pager/image.rb, line 107 def obtain_image_intensity_matrix(img=@img) return Matrix.rows(img.export_pixels(0,0,img.columns,img.rows,"I").each_slice(img.columns).reduce([]) {|x,y| x<<y }) end
rows()
click to toggle source
# File lib/ruby_pager/image.rb, line 29 def rows @img.rows end
save_binary_image(path,method,*params)
click to toggle source
# File lib/ruby_pager/image.rb, line 251 def save_binary_image(path,method,*params) @logger.info("Performing #{method} binarization on image") bin_mat = send(method) to_file(path,intensity_matrix_to_image(bin_mat)) end
standard_deviation_of_region(mean,from_x=@integral_image.row_size-1,from_y=@integral_image.column_size-1,region_height=@integral_image.row_size-1,region_width=@integral_image.column_size-1)
click to toggle source
# File lib/ruby_pager/image.rb, line 79 def standard_deviation_of_region(mean,from_x=@integral_image.row_size-1,from_y=@integral_image.column_size-1,region_height=@integral_image.row_size-1,region_width=@integral_image.column_size-1) #if no parameters are given the input variables get assigned the default values indicated in the above specification and hence return the deviation of the whole image #raise "Region size provided exceeds the image boundaries" if from_y - region_width < 0 or from_x - region_height < 0 res = @sum_of_squares_intensity_matrix[from_x,from_y] region_cuts = 0 if from_y - region_width > 0 res -= @sum_of_squares_intensity_matrix[from_x,from_y-(region_width+1)] region_cuts+=1 end if from_x - region_height > 0 res -= @sum_of_squares_intensity_matrix[from_x-(region_height+1),from_y] region_cuts+=1 end if region_cuts == 2 res += @sum_of_squares_intensity_matrix[from_x-(region_height+1),from_y-(region_width+1)] end #region_width = region_width - from_y if from_y - region_width < 0 # region_height = region_height - from_x if from_x - region_height < 0 return Math.sqrt(((res.to_f/(region_height*region_width).to_f)-mean**2).abs) end
to_file(path,img=@img)
click to toggle source
# File lib/ruby_pager/image.rb, line 116 def to_file(path,img=@img) @logger.info("Saving image to path: #{path}") img.write(path) end
Private Instance Methods
apply_threshold(threshold=0.5,intensity_matrix=@intensity_matrix)
click to toggle source
# File lib/ruby_pager/image.rb, line 146 def apply_threshold(threshold=0.5,intensity_matrix=@intensity_matrix) threshold *= 255 intensity_matrix.map{|val| val < threshold ? 0.0 : 1.0 } end
calculate_integral_images(intensity_matrix=@intensity_matrix)
click to toggle source
# File lib/ruby_pager/image.rb, line 121 def calculate_integral_images(intensity_matrix=@intensity_matrix) if not @integral_calculated calculate_intensity_matrix unless @intensity_calculated @logger.info("Calculating integral images") res = intensity_matrix.clone res2= intensity_matrix.clone res.each_with_index do |val,x,y| res2[x,y]=res2[x,y]**2 if x !=0 and y != 0 res[x,y]+=res[x-1,y]+res[x,y-1]-res[x-1,y-1] res2[x,y]+=res2[x-1,y]+res2[x,y-1]-res2[x-1,y-1] elsif x ==0 and y != 0 res[x,y]+=res[x,y-1] res2[x,y]+=res2[x,y-1] elsif x !=0 and y == 0 res[x,y]+=res[x-1,y] res2[x,y]+=res2[x-1,y] end end @integral_calculated=true @sum_of_squares_intensity_matrix = res2 @integral_image = res end end
niblack_binarization(intensity_matrix=@intensity_matrix,region_height=30,region_width=30)
click to toggle source
# File lib/ruby_pager/image.rb, line 211 def niblack_binarization(intensity_matrix=@intensity_matrix,region_height=30,region_width=30) vec = intensity_matrix.each_with_index.map do |val,x,y| from_x = x+((region_height-1)/2) > intensity_matrix.row_size-1 ? intensity_matrix.row_size-1 : x+((region_height-1)/2) from_y = y+((region_width-1)/2) > intensity_matrix.column_size-1 ? intensity_matrix.column_size-1 : y+((region_width-1)/2) @logger.debug("Im on #{x} #{y} - region is going to start on #{from_x} #{from_y}") mean = mean_of_region(from_x,from_y,region_height,region_width).to_f threshold = (mean - 0.2 * standard_deviation_of_region(mean,from_x,from_y,region_height,region_width).to_f).to_f val < threshold ? 0.0 : 1.0 end return Matrix.rows(vec.each_slice(intensity_matrix.column_size).reduce([]) {|x,y| x += [y] }) end
otsu_binarization(intensity_matrix=@intensity_matrix,*params)
click to toggle source
# File lib/ruby_pager/image.rb, line 185 def otsu_binarization(intensity_matrix=@intensity_matrix,*params) percentage_calculated = 0 csum=0.0 sbmax=0.0 threshold = -1 sum = @histogram.inject(0.0){|res,(key,val)| res += key * val} #total sum of the histogram, in one line :-) @histogram.each do |key,value| #we only visit the keys with values, hence we do not need to check if we have visited all filled buckets percentage_calculated += value.to_f percentage_pending = 1.0- percentage_calculated csum += (key * value).to_f m1 = csum / percentage_calculated.to_f m2 = (sum - csum) / percentage_pending.to_f sb = (percentage_calculated * percentage_pending *((m1 - m2)**2)).to_f if (sb > sbmax) sbmax = sb threshold = key end end threshold/=255.0 return apply_threshold(threshold,intensity_matrix) end
simple_binarization(intensity_matrix=@intensity_matrix,*params)
click to toggle source
# File lib/ruby_pager/image.rb, line 151 def simple_binarization(intensity_matrix=@intensity_matrix,*params) #1. select initial estimate T (between mean gray value and (min+max)/2, depending on the expected area of the objects) median=(intensity_matrix.max+intensity_matrix.max)/2 mean=mean_of_region #uses the method with integral image, no input params means default input params threshold_old = Random.new.rand([mean,median].min..[mean,median].max)#selects a random value between the median and the mean threshold_new = threshold_old begin threshold_old = threshold_new mean_lower = 0; count_lower = 0 ; mean_higher = 0; count_higher = 0; #2. compute average value μ1 of pixels > T and μ2 of pixels ≤ T intensity_matrix.each do |val| if val < threshold_old mean_lower+=val count_lower+=1.0 else mean_higher+=val count_higher+=1.0 end end mean_lower /= count_lower mean_higher /= count_higher #3. T ← (μ1 + μ2)/2 threshold_new = (mean_lower + mean_higher) / 2 end while (threshold_old - threshold_new).abs > 0.0001 #4. repeat from (2) until convergence threshold_new/=255.0 return apply_threshold(threshold_new,intensity_matrix) end