<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Kerry D. Wong &#187; Image Processing</title>
	<atom:link href="http://www.kerrywong.com/tag/image-processing/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.kerrywong.com</link>
	<description></description>
	<lastBuildDate>Fri, 03 Sep 2010 00:51:09 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Canny Edge Detection Auto Thresholding</title>
		<link>http://www.kerrywong.com/2009/05/07/canny-edge-detection-auto-thresholding/</link>
		<comments>http://www.kerrywong.com/2009/05/07/canny-edge-detection-auto-thresholding/#comments</comments>
		<pubDate>Fri, 08 May 2009 02:15:41 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Algorithm]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Image Processing]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=999</guid>
		<description><![CDATA[In the example I gave in &#8220;Interfacing IPP with Magick++&#8220;, I illustrated how to use Intel’s Integrated Performance Primitives (IPP) to perform edge detection. One issue with Canny edge detection algorithm is that we need to specify a high threshold and a low threshold. How to select those threshold values affect the quality of the [...]]]></description>
			<content:encoded><![CDATA[<p>In the example I gave in &#8220;<a href="/2009/03/17/interfacing-ipp-with-magick/">Interfacing IPP with Magick++</a>&#8220;, I illustrated how to use <a href="http://software.intel.com/en-us/intel-ipp/">Intel’s Integrated Performance Primitives (IPP)</a> to perform edge detection. One issue with <a href="http://en.wikipedia.org/wiki/Canny_edge_detector">Canny edge detection</a> algorithm is that we need to specify a high threshold and a low threshold. How to select those threshold values affect the quality of the detected edge greatly. And in my previous example, the threshold values were chosen manually. In this blog post, I will examine a couple of simple methods that can be used to automatically determine the threshold values.<span id="more-999"></span></p>
<p>The simplest way is to use the mean value of the gray scale image pixel values. As a rule of thumb, we set the low threshold to 0.66*[mean value] and set the high threshold to 1.33*[mean value]. Another way is to use the median color in the gray scale image and uses 0.66*[median value] and 1.33*[median value] accordingly. For typical images, these two methods achieve comparable results.</p>
<p>For example, the following shows the picture of a building along with its histogram (original image from <a href="ftp://ftp.research.microsoft.com/pub/download/orid">Microsoft Research Digital Image</a>. Please see <a href="http://research.microsoft.com/en-us/um/people/antcrim/data_objrec/msr%20cambridge%20eula%20for%20digital%20images_download.rtf">Microsoft Research Digital Image License Agreement</a> for more information):</p>
<table>
<tr>
<td>
<div id="attachment_1011" class="wp-caption aligncenter" style="width: 330px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_gray.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_gray.jpg" alt="Building" title="Building" width="320" height="240" class="size-full wp-image-1011" /></a><p class="wp-caption-text">Building</p></div>
</td>
<td>
<div id="attachment_1012" class="wp-caption aligncenter" style="width: 330px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_hist.png"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_hist.png" alt="Building Histogram" title="Building Histogram" width="320" height="240" class="size-full wp-image-1012" /></a><p class="wp-caption-text">Building Histogram</p></div>
</td>
</tr>
</table>
<p>The following shows the edge detection results using Canny algorithm (left image uses mean value auto-thresholding, right image uses median value auto-thresholding) and the results exhibit very little visible differences. </p>
<table>
<tr>
<td>
<div id="attachment_1006" class="wp-caption aligncenter" style="width: 330px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_canny_mean.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_canny_mean.jpg" alt="Building (Canny Mean)" title="Building (Canny Mean)" width="320" height="240" class="size-full wp-image-1006" /></a><p class="wp-caption-text">Building (Canny Mean)</p></div>
</td>
<td>
<div id="attachment_1007" class="wp-caption aligncenter" style="width: 330px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_canny_median.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_canny_median.jpg" alt="Building (Canny Median)" title="Building (Canny Median)" width="320" height="240" class="size-full wp-image-1007" /></a><p class="wp-caption-text">Building (Canny Median)</p></div>
</td>
</tr>
</table>
<p>However, for images has non-equalized histogram (see the picture of cloud and its histogram below): </p>
<table>
<tr>
<td>
<div id="attachment_1018" class="wp-caption aligncenter" style="width: 250px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_gray.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_gray.jpg" alt="Cloud" title="Cloud" width="240" height="320" class="size-full wp-image-1018" /></a><p class="wp-caption-text">Cloud</p></div>
</td>
<td>
<div id="attachment_1019" class="wp-caption aligncenter" style="width: 330px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_hist.png"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_hist.png" alt="Cloud Histogram" title="Cloud Histogram" width="320" height="240" class="size-full wp-image-1019" /></a><p class="wp-caption-text">Cloud Histogram</p></div>
</td>
</tr>
</table>
<p>Canny Edge detection result based on mean value auto-thresholding is pretty poor (see image on the left below), while edge detection based on median value auto-thresholding achieved much better result (see image on the right below)</p>
<table>
<tr>
<td>
<div id="attachment_1021" class="wp-caption aligncenter" style="width: 250px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_canny_mean.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_canny_mean.jpg" alt="Cloud (Canny Mean)" title="Cloud (Canny Mean)" width="240" height="320" class="size-full wp-image-1021" /></a><p class="wp-caption-text">Cloud (Canny Mean)</p></div>
</td>
<td>
<div id="attachment_1024" class="wp-caption aligncenter" style="width: 250px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq_canny_median.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq_canny_median.jpg" alt="Cloud (Canny Median)" title="Cloud (Canny Median)" width="240" height="320" class="size-full wp-image-1024" /></a><p class="wp-caption-text">Cloud (Canny Median)</p></div>
</td>
</tr>
</table>
<p>Alternatively, we could have performed histogram equalization on the image first before applying Canny edge detection with mean auto-thresholding:</p>
<table>
<tr>
<td>
<div id="attachment_1026" class="wp-caption aligncenter" style="width: 250px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq.png"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq.png" alt="Cloud Equalized" title="Cloud Equalized" width="240" height="320" class="size-full wp-image-1026" /></a><p class="wp-caption-text">Cloud Equalized</p></div>
</td>
<td>
<div id="attachment_1028" class="wp-caption aligncenter" style="width: 330px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_hist_eq.png"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_hist_eq.png" alt="Cloud Equalized Histogram" title="Cloud Equalized Histogram" width="320" height="240" class="size-full wp-image-1028" /></a><p class="wp-caption-text">Cloud Equalized Histogram</p></div>
</td>
</tr>
</table>
<p>And after image equalization, both mean and median value auto-thresholding achieved similar results.</p>
<table>
<tr>
<td>
<div id="attachment_1030" class="wp-caption aligncenter" style="width: 250px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq_canny_mean.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq_canny_mean.jpg" alt="Cloud Equalized (Canny Mean)" title="Cloud Equalized (Canny Mean)" width="240" height="320" class="size-full wp-image-1030" /></a><p class="wp-caption-text">Cloud Equalized (Canny Mean)</p></div>
</td>
<td>
<div id="attachment_1031" class="wp-caption aligncenter" style="width: 250px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq_canny_median.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq_canny_median.jpg" alt="Cloud Equalized (Canny Median)" title="Cloud Equalized (Canny Median)" width="240" height="320" class="size-full wp-image-1030" /></a><p class="wp-caption-text">Cloud Equalized (Canny Median)</p></div>
</td>
</tr>
</table>
<p>So the Canny edge detection using median value auto-thresholding seems to adapt to different types of images very well (note selecting the median value selection can be thought as equalizing the histogram, except that the pixel values are not changed during such operation).</p>
<p>The following is the code listing for the histogram calculation using IPP (based on the <a href="/2009/04/10/an-image-class-based-on-ipp/">image class I created earlier</a>)</p>
<pre class="brush: cpp;">
/** @brief Get the min max mean value statistics for the current image
 *   @param min, max, mean: these are output parameters that are passed
 *         back by reference.
 */
void IPPGrayImage::MinMaxMean(float&amp; min, float&amp; max, double&amp; mean)
{
    IppStatus sts;
    IppiSize origImgSize = {_width, _height};

    sts = ippiMinMax_32f_C1R(_imgBuffer, _width * PIXEL_SIZE, origImgSize, &amp;min, &amp;max);
    assert(sts == ippStsNoErr);
    sts = ippiMean_32f_C1R(_imgBuffer, _width * PIXEL_SIZE, origImgSize, &amp;mean, ippAlgHintFast);
    assert(sts == ippStsNoErr);
}

/** @brief Calculate the histogram of the image
 *   @param nLevel: the number of bins in the histogram
 *        levels: this is the optional levels user can pass in (histogram will be then
 *                calculated with these levels instead of the uniform levels by default.
 *   @return an integer array which contains the histogram.
 *   @note the histogram is by default calculated using uniform bins across the color range.
 */
unsigned int * IPPGrayImage::GetHistogram(unsigned int nLevel, float levels[])
{
    IppStatus sts;
    Ipp32f* l = new Ipp32f[nLevel];
    IppiSize origImgSize = {_width, _height};

    unsigned int* bins = new unsigned int[nLevel - 1];

    float minVal = 0, maxVal = 0, stepVal = 0;
    double meanVal = 0;
    if (levels != NULL)
    {
        for (unsigned int i = 0; i &lt; nLevel; i++)
        {
            l[i] = levels[i];
        }
    }
    else
    {
        MinMaxMean(minVal, maxVal, meanVal);
        stepVal = (maxVal - minVal) / (float) nLevel;

        for (unsigned int i = 0; i &lt; nLevel; i++)
        {
            l[i] = minVal + stepVal * i;
        }
    }

    sts = ippiHistogramRange_32f_C1R(_imgBuffer, _width * PIXEL_SIZE, origImgSize, (Ipp32s*) bins, (Ipp32f*) l, nLevel);
    return bins;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/05/07/canny-edge-detection-auto-thresholding/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Interfacing IPP with Magick++</title>
		<link>http://www.kerrywong.com/2009/03/17/interfacing-ipp-with-magick/</link>
		<comments>http://www.kerrywong.com/2009/03/17/interfacing-ipp-with-magick/#comments</comments>
		<pubDate>Wed, 18 Mar 2009 01:01:24 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Algorithm]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Image Processing]]></category>
		<category><![CDATA[IPP]]></category>
		<category><![CDATA[Multi-threading]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=864</guid>
		<description><![CDATA[Intel&#8217;s Integrated Performance Primitives (IPP) is a low level C++ library. It provides routines that are highly optimized on Intel processors. I recently started using it because its vast speed advantage in signal and image processing applications. Since the implementation of many of the functions are threaded, it makes the task of writing high performance [...]]]></description>
			<content:encoded><![CDATA[<p>Intel&#8217;s <a href="http://www.intel.com/cd/software/products/asmo-na/eng/302910.htm">Integrated Performance Primitives (IPP)</a> is a low level C++ library. It provides routines that are highly optimized on Intel processors. I recently started using it because its vast speed advantage in signal and image processing applications.<span id="more-864"></span></p>
<p>Since the implementation of many of the functions are threaded, it makes the task of writing high performance applications much easier. Since it is a set of &#8220;performance primitives&#8221; as the name suggests, it uses its own data structures (e.g. Ipp8u) and does not provide functions to directly inter-operate with data coming from other sources (e.g. image files).</p>
<p>Fortunately, such data conversion is pretty straight forward. In this post, I will illustrate how to convert an image file (e.g. .jpg, .png, .gif) to the data IPP uses, and how to save the result into a standard image file once the processing is done.</p>
<p><a href="http://www.imagemagick.org/Magick%2B%2B/">Magick++</a> is a very comprehensive image-processing C++ library and the Image class it provides handles image files quite well. So I chose to use Magic++&#8217;s API to convert image files to the data structure IPP uses. In this particular example, I will use a gray level image. But in practice, color images can be easily handled in a similar fashion.</p>
<p>For the code mentioned below, the following headers and namespaces are used:</p>
<pre class="brush: cpp;">
#include &lt;Magick++/Image.h&gt;
#include &lt;Magick++.h&gt;
#include &lt;ipp.h&gt;

using namespace std;
using namespace Magick;
</pre>
<p>And the following code shows how to convert a standard image file data into format that is suitable for IPP.</p>
<pre class="brush: cpp;">
    IppStatus sts;
    Image img(&quot;{Image File Name}&quot;);
    Geometry g = img.size();

    unsigned int width = g.width();
    unsigned int height= g.height();

    Pixels view(img);
    PixelPacket *pixels = view.get(0,0,width,height);

    int stepByte = 0;
    //allocating a buffer of unsigned char (Ipp8u) for the image.
    Ipp8u *imgCache = ippiMalloc_8u_C1(width,height, &amp;stepByte);

    for (unsigned int row = 0; row &lt; height ; row++)
    {
        for (unsigned int column = 0; column &lt; width ; column++)
        {
            PixelPacket *p = &amp;pixels[column + row * width];
            Color c = Color(p-&gt;red, p-&gt;green, p-&gt;blue);
            double i = c.scaleQuantumToDouble(c.intensity()) * 255;
            imgCache[column + row * width] = (char) i;
        }
    }
</pre>
<p>The above code snippet first reads the image data into *PixelPacket and the pixel buffer is then converted into a one channel buffer of chars (color value 0-255). Note that the range of the image data Magick++ reads in is between 0 and <strong><em>QuantumRange</em></strong>, which needs to be converted back to the range 0-255 accepted by the Ipp8u buffer. If other types of <strong><em>IPP</em></strong> image buffers are used (e.g. Ipp32f), this <strong><em>scaleQuantumToDouble()</em></strong> conversion may not be necessary. At the end, the image data is converted into the one dimensional array <strong><em>imgCache</em></strong> which can be used by <em>IPP</em> procedures.</p>
<p>Once we are in the <strong><em>IPP</em></strong> data domain, we can proceed with whatever processing we had in mind. Here I will show the code for edge detection using <a href="http://en.wikipedia.org/wiki/Canny_edge_detector">Canny algorithm</a>.</p>
<pre class="brush: cpp;">
    IppiSize orgImgSize = {width, height};
    IppiSize newImgSize = {width + 2, height + 2};

    Ipp8u *imgCache1 = ippiMalloc_8u_C1(width + 2,height + 2, &amp;stepByte);
    sts = ippiCopyReplicateBorder_8u_C1R(imgCache, width, orgImgSize,  imgCache1,
            width + 2 , newImgSize, 2, 2);
    IppiSize roi = {width, height};

    Ipp32f low=30.0f, high=100.0f;
    int size, size1, srcStep, dxStep, dyStep, dstStep;

    Ipp8u *src, *dst, *buffer;
    Ipp16s *dx, *dy;

    sts = ippiFilterSobelNegVertGetBufferSize_8u16s_C1R(
            roi, ippMskSize3x3, &amp;size);
    sts = ippiFilterSobelHorizGetBufferSize_8u16s_C1R(
            roi, ippMskSize3x3, &amp;size1);

    if (size&lt;size1) size=size1;
    ippiCannyGetSize(roi, &amp;size1);
    if (size&lt;size1) size=size1;

    buffer = ippsMalloc_8u(size);
    dx = ippsMalloc_16s(size);
    dy = ippsMalloc_16s(size);
    dst = ippsMalloc_8u(size);

    sts = ippiFilterSobelNegVertBorder_8u16s_C1R (
            imgCache1, width + 2 , dx, (width + 2) * 2 ,
            roi, ippMskSize3x3, ippBorderRepl, 0, buffer);
    sts = ippiFilterSobelHorizBorder_8u16s_C1R(
            imgCache1, width + 2, dy, (width + 2) *2 ,
            roi, ippMskSize3x3, ippBorderRepl, 0, buffer);
    sts = ippiCanny_16s8u_C1R(dx,
            (width + 2) * 2, dy, (width + 2) * 2,
            dst, width, roi, low, high, buffer);
</pre>
<p>The code shown above is adopted from Intel&#8217;s IPP manual for image and video processing (by default it is located at /opt/intel/ipp/<em>{version number}</em>/em64t/doc/ippiman.pdf).</p>
<p>Please pay special attention to how the original image is extended via <strong><em>ippiCopyReplicateBorder_8u_C1R</em></strong>. The image is extended by 2 pixels in each direction because the 3&#215;3 mask used for the filtering operation. I omitted error checking code for simplicity, but in general you need to check the return status of each ippi function call. When the call is successful, the status is <strong><em>ippStsNoErr</em></strong>. If you receive a value other than <strong><em>ippStsNoErr</em></strong> (e.g. <strong><em>ippStsStepErr</em></strong>) you will need to check your buffer size to make sure that they are adjusted according to the data type. For instance, in the code above <strong><em>dx</em> </strong>and <strong><em>dy</em></strong> are both 16bit signed integers and thus they both occupy two bytes each. </p>
<p>To save the result image, we convert the pixel buffer back to <strong><em>PixelPacket</em></strong> type. Again we need to convert the data in the buffer (<strong><em>Ipp8u</em></strong>) to the range accepted by Magick++ API.</p>
<pre class="brush: cpp;">
    for (unsigned int y = 0; y&lt; height ; y++)
    {
        for (unsigned int x = 0; x &lt; width; x++)
        {
            Color c;
            float clr = (float) dst[x + y * width] /255;
            float q = c.scaleDoubleToQuantum(clr);
            pixels[x+ y * width] = Color(q, q, q);
        }
    }

    view.sync();
    img.syncPixels();

    img.write(&quot;{Image File Name}&quot;);
</pre>
<p>We can also save the result data into a new image (instead of syncing the data back to the image object holding the original image data) using the code below:</p>
<pre class="brush: cpp;">
    Image img1(Geometry(width, height),&quot;white&quot;);

    for (unsigned int y = 0; y&lt; height ; y++)
    {
        for (unsigned int x = 0; x &lt; width; x++)
        {
            Color c;
            float clr = (float) dst[x + y * width] /255;
            float q = c.scaleDoubleToQuantum(clr);
            img1.pixelColor(x,y, Color(q, q, q));
        }
    }

    img1.write(&quot;{Image File Name}&quot;);
</pre>
<p>And finally the buffers used are freed.</p>
<pre class="brush: cpp;">
    ippiFree(imgCache);
    ippsFree(buffer);
</pre>
<p>The following images show Canny edge detector in action using the code in this article:</p>
<div id="attachment_876" class="wp-caption aligncenter" style="width: 573px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/03/test.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/03/test.jpg" alt="Original" title="Original" width="563" height="422" class="size-full wp-image-876" /></a><p class="wp-caption-text">Original</p></div>
<div id="attachment_879" class="wp-caption aligncenter" style="width: 573px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/03/test_canny.png"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/03/test_canny.png" alt="Canny Edge Detector applied" title="Canny Edge Detector applied" width="563" height="422" class="size-full wp-image-879" /></a><p class="wp-caption-text">Canny Edge Detector applied</p></div>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/03/17/interfacing-ipp-with-magick/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
