Skip to content

Instantly share code, notes, and snippets.

@jeffkreeftmeijer
Created March 30, 2011 17:44
Show Gist options
  • Save jeffkreeftmeijer/894864 to your computer and use it in GitHub Desktop.
Save jeffkreeftmeijer/894864 to your computer and use it in GitHub Desktop.
Counting blobs in an image in Ruby
require 'chunky_png'
image = ChunkyPNG::Image.from_file('blobs.png')
# First, we need to map the image to data we can use. We'll give any pixel that
# is (completely) black in the original image a value of 0 and the pixels that
# have color get a value of 1 (in the blobs_bw.png image, this is illustrated
# by making every colored pixel black and leaving the others transparent):
image.pixels.map! { |pixel| pixel == ChunkyPNG::Color::BLACK ? 0 : 1 }
# Now, the image's pixels are mapped to a matrix. Here's the whole thing, by
# row:
#
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0]
# [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0]
# [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0]
# [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0]
# [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0]
# [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0]
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0]
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0]
# [0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0]
# [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0]
# [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
# [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# To calculate how many blobs are in this image, we need to count the corners
# in the image (because the blobs are _not_ round, they're built up from square
# pixels, remember?):
corners = 0
image.height.times do |row|
image.row(row).each_with_index do |pixel, index|
# For every pixel on every row, we figure out its neighborhood. In this
# case, we're using a 2x2 one, so we'll get the row and pixel index and
# add one to get the pixel on the right of the current one, the pixel below
# and the one right from the one below. We'll store the sum of the pixel
# values in `neighborhood`.
if image.include_x?(index + 1) && image.include_y?(row + 1)
neighborhood =
image[index, row] +
image[index + 1, row] +
image[index, row + 1] +
image[index + 1, row + 1]
# Recognising an external or internal corner is easy. An external one
# looks like the one on the left and an internal one looks like the one
# on the right:
#
# [0,0] [0,1]
# [0,1] [1,1]
#
# Using that, we know that a `neighborhood` with a value of 3 represents
# an internal corner and one with a value of 1 means it's an external
# corner.
#
# After counting the corners, we subtract the internal corners from the
# external ones:
corners += 1 if neighborhood == 1
corners -= 1 if neighborhood == 3
end
end
end
# The output will be a multiple of four, so dividing the result by four will
# give us the result we're looking for:
puts corners / 4 # 3
# If you have any comments or know a way to improve the code, please let me
# know! I'm still totally new to this stuff myself but I decided to write about
# it while learning to better understand it myself. Why I'm not publishing this
# on my blog? I don't know.. Would you like to read more about this stuff? :)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment