× {{alert.msg}} Never ask again
Get notified about new tutorials RECEIVE NEW TUTORIALS

Finding dark purple pixels in an image

Ray Phan
Feb 09, 2015
<p>Here's something to get you started. Let's go with the theme of colour segmentation where you only want to extract pixels that are of a deep purple. I would like to point you to the <a href="http://en.wikipedia.org/wiki/HSL_and_HSV" rel="nofollow">HSV colour space</a> before we get started. The HSV colour space is ideal for representing colours in a way that is most intuitive to humans. We tend to describe colours by their dominant colour, followed by attributes such as how washed out or how pure the colour is, and how bright or dark the colour is. The dominant colour is represented by the Hue, the appearance of how washed out or how pure the colour is is represented by the Saturation and the intensity of the colour is represented by the Value, and hence <strong>H</strong>ue-<strong>S</strong>aturation-<strong>V</strong>alue, or the HSV colour space.</p> <p>We can transform a RGB image so that it becomes HSV by <a href="http://www.mathworks.com/help/matlab/ref/rgb2hsv.html" rel="nofollow"><code>rgb2hsv</code></a>. This will return a 3D matrix that has the hue, saturation and value as 2D slices in a 3D matrix, much like a RGB image where each slices represents the red, green and blue channels. Let's see what each component looks like once we transform the image into HSV:</p> <pre><code>im = imread('http://www.cdc.gov/dpdx/images/malaria/ovale/Po_gametocyte_thickB.jpg'); hsv = rgb2hsv(im2double(im)); figure; for idx = 1 : 3 subplot(1,3,idx); imshow(hsv(:,:,idx)); end </code></pre> <p>The first line of code reads in an image from a URL. I'm going to use the one that Hoki referred you to, as it's the most simplest one to deal with. For self-containment, this is what the original image looks like:</p> <p><img src="http://www.cdc.gov/dpdx/images/malaria/ovale/Po_gametocyte_thickB.jpg" alt=""></p> <p>Once we do this, we convert the image into the HSV colour space. It is important that you convert the image to <code>double</code> precision and you normalize each component to <code>[0,1]</code>, and that is performed by <a href="http://www.mathworks.com/help/matlab/ref/im2double.html" rel="nofollow"><code>im2double</code></a>. Next, we spawn a new figure, and place each component in a single row over three columns. The first column represents the hue, next column the saturation and finally the last column being the value. This is the figure that we see:</p> <p><img src="http://i.stack.imgur.com/brlwo.png" alt="enter image description here"></p> <p>With the first figure, it looks like the dominant colour is purple, whether it's a light shade or a dark shade of the colour, so the hue won't help us here. If you look at a HSV colour wheel:</p> <p><img src="http://www.hobbitsandhobos.com/wp-content/uploads/2011/06/colorWheel.png" alt=""></p> <p>Normalize the wheel so that it falls between <code>[0,1]</code> instead of 0 to 360 degrees. The hue is actually represented as degrees due to the nature of the colour space, but MATLAB normalizes this to <code>[0,1]</code>. You can see that purple falls within a hue of <code>[0.6,0.8]</code>, which corresponds to the first figure I showed you that displays the hue for our image. If you examine the pixels around the image, they fluctuate between this range. Therefore, the hue won't help us much here.</p> <p>What will certainly help us are the saturation and value components. If you take a look, the deep purple pixels have a higher saturation than the rest of the background, which makes sense because the deep purple has a much more pure version of purple than the rest of the background. For the value, you can see that the brightness of the dark purple is darker than the background.</p> <p>We can use these two points as an exploit to segment out the purple colour in the image. The easiest thing to do would be to threshold the saturation and value planes so that any values that are within a certain range you keep while those that are outside you throw away. Therefore, you can do something like this:</p> <pre><code>sThresh = hsv(:,:,2) &gt; 0.6 &amp; hsv(:,:,2) &lt; 0.9; vThresh = hsv(:,:,3) &gt; 0.4 &amp; hsv(:,:,3) &lt; 0.65; </code></pre> <p>I used <a href="http://www.mathworks.com/help/images/ref/impixelinfo.html" rel="nofollow"><code>impixelinfo</code></a> and I hovered my mouse over the saturation and value components to examine what the values were for the deep purple regions. It looks like those pixels that are deep purple have a saturation value between 0.6 and 0.9, while the value component has values between 0.4 and 0.65. The above code will create two binary masks where <code>true</code> means that the pixel satisfies our criteria while <code>false</code> means it doesn't. Because I want to combine both things together and not leave any stone unturned, let's logical OR the masks together for the final result:</p> <pre><code>figure; result = sThresh | vThresh; imshow(result); </code></pre> <p>We will also show the result too. This is what we get:</p> <p><img src="http://i.stack.imgur.com/45KMH.png" alt="enter image description here"></p> <p>As you can see, this does a pretty good job, but we have remnants of the red arrow that we don't want in the final result. To do a bit of cleanup, we can use morphology - specifically an opening filter of a small window so that we don't affect the pixels that we want as much. We can use <a href="http://www.mathworks.com/help/images/ref/imopen.html" rel="nofollow"><code>imopen</code></a> to perform our opening operation for us. A morphological opening removes isolated pixels that appear around your image. You use what is called a <a href="http://en.wikipedia.org/wiki/Structuring_element" rel="nofollow">structuring element</a> that is used to look at local neighbourhoods of your image. For the basics, any pixel regions that are as small as the shape that is contained within the structuring element get removed. Because we want to preserve the shape of the other objects, we can try using a 5 x 5 disk structuring element to clean these pixels up:</p> <pre><code>figure; se = strel('disk', 2, 0); final = imopen(result, se); imshow(final); </code></pre> <p>This is what we get:</p> <p><img src="http://i.stack.imgur.com/HYMn4.png" alt="enter image description here"></p> <p>Not bad! There are some holes that we need to patch up, so let's fill in those holes with <a href="http://www.mathworks.com/help/images/ref/imfill.html" rel="nofollow"><code>imfill</code></a>:</p> <pre><code>figure; final_noholes = imfill(final, 'holes'); imshow(final_noholes); </code></pre> <p>This is what we get:</p> <p><img src="http://i.stack.imgur.com/ameDe.png" alt="enter image description here"></p> <p>OK! So we have our mask. The last thing we need to do is present the image so that you only show the deep purple colours from the original image, and nothing else. That can easily be achieved with <a href="http://www.mathworks.com/help/matlab/ref/bsxfun.html" rel="nofollow"><code>bsxfun</code></a>:</p> <pre><code>figure; out = bsxfun(@times, im, uint8(final_noholes)); imshow(out); </code></pre> <p>The above operation takes your mask, and multiplies every pixel in your image by this mask. One small thing I'd like to point out is that the mask we found in the previous step needs to be cast to <code>uint8</code>, because <code>bsxfun</code> requires that the multiplication (or whatever operation you perform) need to be <strong>the same type</strong>. We replicate this mask in 3D so that you mask out the unwanted RGB pixels and only keep the ones you are looking for.</p> <p>This is what we finally get:</p> <p><img src="http://i.stack.imgur.com/uFywK.png" alt="enter image description here"></p> <p>As you can see, it isn't perfect, but it's certainly enough to get you started. Those thresholds are what are important, but with some very simple thresholding, I extracted most of the purple pixels out.</p> <hr> <p>To make it easier for you, here's the code that I wrote above that can easily be copied and pasted into MATLAB for you to run:</p> <pre><code>clear all; close all; clc; im = imread('http://www.cdc.gov/dpdx/images/malaria/ovale/Po_gametocyte_thickB.jpg'); hsv = rgb2hsv(im2double(im)); figure; for idx = 1 : 3 subplot(1,3,idx); imshow(hsv(:,:,idx)); end sThresh = hsv(:,:,2) &gt; 0.6 &amp; hsv(:,:,2) &lt; 0.9; vThresh = hsv(:,:,3) &gt; 0.4 &amp; hsv(:,:,3) &lt; 0.65; figure; result = sThresh | vThresh; imshow(result); figure; se = strel('disk', 2, 0); final = imopen(result, se); imshow(final); figure; final_noholes = imfill(final, 'holes'); imshow(final_noholes); figure; out = bsxfun(@times, im, uint8(final_noholes)); imshow(out); </code></pre> <hr> <p>Good luck!</p> <p>This tip was originally posted on <a href="http://stackoverflow.com/questions/28283454/Finding%20dark%20purple%20pixels%20in%20an%20image/28284626">Stack Overflow</a>.</p>
comments powered by Disqus