views:

249

answers:

3

The problem

I have a collection of digital photos of a mountain in Japan. However the mountain is often obscured by clouds or fog.

What techniques can I use to detect that the mountain is visible in the image? I am currently using Perl with the Imager module, but open to alternatives.

All the images are taken from the exact same position - these are some samples.

Sample Images

My naïve solution

I started by taking several horizontal pixel samples of the mountain cone and comparing the brightness values to other samples from the sky. This worked well for differentiating good image 1 and bad image 2.

However in the autumn it snowed and the mountain became brighter than the sky, like image 3, and my simple brightness test started to fail.

Image 4 is an example of an edge case. I would classify this as a good image since some of the mountain is clearly visible.

UPDATE 1

Thank you for the suggestions - I am happy you all vastly over-estimated my competence.

Based on the answers, I have started trying the ImageMagick edge-detect transform, which gives me a much simpler image to analyze.

convert sample.jpg -edge 1 edge.jpg

Edge detected samples

I assume I should use some kind of masking to get rid of the trees and most of the clouds.

Once I have the masked image, what is the best way to compare the similarity to a 'good' image? I guess the "compare" command suited for this job? How do I get a numeric 'similarity' value from this?

UPDATE 2

I think I may be getting somewhere with convolve.

I made my 'kernel' image (top of the image below) by performing edge detect on a good image. I then blacked out all the 'noise' around the outline of the mountain and then cropped it.

I then used the following code:

use Image::Magick;

# Edge detect the test image
my $test_image = Image::Magick->new;
$test_image->Read($ARGV[0]);
$test_image->Quantize(colorspace=>'gray');
$test_image->Edge(radius => 1);

# Load the kernel
my $kernel_image = Image::Magick->new;
$kernel_image->Read('kernel-crop.jpg');

# Convolve and show the result
$kernel_image->Convolve(coefficients => [$test_image->GetPixels()]);
$kernel_image->Display();

I ran this for various sample images, and I got results as below (the convolved image is shown below each sample):

(Sorry - different sample images from last time!)

alt text

Now I am trying to quantify how 'ridgy' an image is. I tried taking the image average brightness:

$kernel_image->Scale('1x1');
die $kernel_image->GetPixel(x=>1,y=>1)[0];

But this gives does not give meaningful values (0.0165, 0.0175 and 0.0174). Any better ways?

+8  A: 

I think you are working on too low a level. A quick pass through an edge detection filter partitioned the image set very distinctly into (1, 3) and (2, 4). Especially if these images come from a fixed camera viewpoint, finding a match against the prototypical shape in (1) would be relatively easy algorithmically. Even your case of (4) could give you a domain of partial matching which you could heuristically determine if there was enough mountain there to consider.

msw
Thank you - I have started playing with edge detect and updated the question. Still a little stuck on how to quantify how much mountain is there.
Gavin Brock
+4  A: 

The answer depends on how specific the problem is. If it's the same mountain from the same POV, run and edge detection against a known good image, and use it as a baseline for convolving against edge-detected images from the corpus. If it's only the edge of the mountain that you're interested in, manually remove other features from the baseline.

Marcelo Cantos
Thank you. Luckily the problem is very specific - the POV is fixed. Edge detection seems to be the right place to start. I'm little unsure of the convolving part.
Gavin Brock
(Sorry for the delayed response; different time zone.) The convolution of two images will exhibit spikes that indicate some kind of similarity. If all you want to know is whether enough of the mountain is visible, compute the convolution between an image containing just the mountain's edge and the image you're testing. A strong spike near the center of the image will tell all.
Marcelo Cantos
+4  A: 

A few specific recommendations, building upon what you've got already:

  1. Take your best image (something like image 1), run it through edge detection, open the result in any graphic editor (MS Paint will do) and clean everything except the mountain top boundary (the "chinese hat" line). This is you convolution kernel. You can crop it (not resize!) from above and below to save some time in the next step.
  2. Use the Convolve function from PerlMagick (you seem already comfortable with Perl and ImageMagick) to convolve the kernel with a few images. On the resulting image you should see a sharp spike corresponding to the "correct" position of the kernel (coinciding with the mountain in the image).
  3. The relative (to the level of surrounding noise) height of this spike will be larger when the mountain is better visible. By taking several representative images you might be able to determine a threshold that will separate good images from the bad ones.
  4. Whatever you do, there will be false positives and false negatives. Be prepared.
AVB
Thank you for the step-by-step guide. This is really helpful for the level I am at. However I am a little unsure how step 2 works - how do I pass the 'kernel' image to the Convolve function - it seems to only take a matrix of coefficients?
Gavin Brock
@Gavin: Kernel == matrix. I've tried to find some suitable explanation of convolutions online for you, but couldn't find anything -- maybe you should try yourself.
AVB
After much hacking, I used $test_image->GetPixels() as the coefficients - is that valid? I updated the post again.
Gavin Brock
It seems to work, well done. It doesn't really matter much, but you should be doing the convolution the other way round, like `test_image->Convolve(coefficients => [$kernel_image->GetPixels()]);`. Also, are you sure that `Scale('1x1')` gives you the average brightness? Anyway, something like 90th percentile should be a better indicator than the average.
AVB