959 lines
108 KiB
HTML
959 lines
108 KiB
HTML
|
|
|
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
|
|
<title>Tutorial: Image Gradient - Boost.GIL documentation</title>
|
|
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
|
<link rel="stylesheet" href="../_static/style.css" type="text/css" />
|
|
<script type="text/javascript">
|
|
var DOCUMENTATION_OPTIONS = {
|
|
URL_ROOT: '../',
|
|
VERSION: '',
|
|
COLLAPSE_MODINDEX: false,
|
|
FILE_SUFFIX: '.html'
|
|
};
|
|
</script>
|
|
<script type="text/javascript" src="../_static/jquery.js"></script>
|
|
<script type="text/javascript" src="../_static/underscore.js"></script>
|
|
<script type="text/javascript" src="../_static/doctools.js"></script>
|
|
<link rel="index" title="Index" href="../genindex.html" />
|
|
<link rel="search" title="Search" href="../search.html" />
|
|
<link rel="top" title="Boost.GIL documentation" href="../index.html" />
|
|
<link rel="next" title="Naming Conventions" href="../naming.html" />
|
|
<link rel="prev" title="Tutorial: Histogram" href="histogram.html" />
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<table border="0" cellpadding="7" cellspacing="0" width="100%" summary=
|
|
"header">
|
|
<tr>
|
|
<td valign="top" width="300">
|
|
<h3><a href="../index.html"><img
|
|
alt="C++ Boost" src="../_static/gil.png" border="0"></a></h3>
|
|
</td>
|
|
|
|
<td >
|
|
<h1 align="center"><a href="../index.html"></a></h1>
|
|
</td>
|
|
<td>
|
|
<div id="searchbox" style="display: none">
|
|
<form class="search" action="../search.html" method="get">
|
|
<input type="text" name="q" size="18" />
|
|
<input type="submit" value="Search" />
|
|
<input type="hidden" name="check_keywords" value="yes" />
|
|
<input type="hidden" name="area" value="default" />
|
|
</form>
|
|
</div>
|
|
<script type="text/javascript">$('#searchbox').show(0);</script>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<hr/>
|
|
<div class="content">
|
|
<div class="navbar" style="text-align:right;">
|
|
|
|
|
|
<a class="prev" title="Tutorial: Histogram" href="histogram.html"><img src="../_static/prev.png" alt="prev"/></a>
|
|
<a class="next" title="Naming Conventions" href="../naming.html"><img src="../_static/next.png" alt="next"/></a>
|
|
|
|
</div>
|
|
|
|
<div class="section" id="tutorial-image-gradient">
|
|
<h1>Tutorial: Image Gradient</h1>
|
|
<div class="contents local topic" id="contents">
|
|
<ul class="simple">
|
|
<li><a class="reference internal" href="#interface-and-glue-code" id="id1">Interface and Glue Code</a></li>
|
|
<li><a class="reference internal" href="#first-implementation" id="id2">First Implementation</a></li>
|
|
<li><a class="reference internal" href="#using-locators" id="id3">Using Locators</a></li>
|
|
<li><a class="reference internal" href="#creating-a-generic-version-of-gil-algorithms" id="id4">Creating a Generic Version of GIL Algorithms</a></li>
|
|
<li><a class="reference internal" href="#image-view-transformations" id="id5">Image View Transformations</a></li>
|
|
<li><a class="reference internal" href="#d-pixel-iterators" id="id6">1D pixel iterators</a></li>
|
|
<li><a class="reference internal" href="#stl-equivalent-algorithms" id="id7">STL Equivalent Algorithms</a></li>
|
|
<li><a class="reference internal" href="#color-conversion" id="id8">Color Conversion</a></li>
|
|
<li><a class="reference internal" href="#image" id="id9">Image</a></li>
|
|
<li><a class="reference internal" href="#virtual-image-views" id="id10">Virtual Image Views</a></li>
|
|
<li><a class="reference internal" href="#run-time-specified-images-and-image-views" id="id11">Run-Time Specified Images and Image Views</a></li>
|
|
<li><a class="reference internal" href="#conclusion" id="id12">Conclusion</a></li>
|
|
</ul>
|
|
</div>
|
|
<p>This comprehensive (and long) tutorial will walk you through an example of
|
|
using GIL to compute the image gradients.</p>
|
|
<p>We will start with some very simple and non-generic code and make it more
|
|
generic as we go along. Let us start with a horizontal gradient and use the
|
|
simplest possible approximation to a gradient - central difference.</p>
|
|
<p>The gradient at pixel x can be approximated with the half-difference of its
|
|
two neighboring pixels:</p>
|
|
<div class="highlight-c++"><div class="highlight"><pre><span class="n">D</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">I</span><span class="p">[</span><span class="n">x</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">I</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="mi">1</span><span class="p">])</span> <span class="o">/</span> <span class="mi">2</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>For simplicity, we will also ignore the boundary cases - the pixels along the
|
|
edges of the image for which one of the neighbors is not defined. The focus of
|
|
this document is how to use GIL, not how to create a good gradient generation
|
|
algorithm.</p>
|
|
<div class="section" id="interface-and-glue-code">
|
|
<h2><a class="toc-backref" href="#id1">Interface and Glue Code</a></h2>
|
|
<p>Let us first start with 8-bit unsigned grayscale image as the input and 8-bit
|
|
signed grayscale image as the output.</p>
|
|
<p>Here is how the interface to our algorithm looks like:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="cp">#include</span> <span class="cpf"><boost/gil.hpp></span><span class="cp"></span>
|
|
<span class="k">using</span> <span class="k">namespace</span> <span class="n">boost</span><span class="o">::</span><span class="n">gil</span><span class="p">;</span>
|
|
|
|
<span class="kt">void</span> <span class="nf">x_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="n">gray8s_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">assert</span><span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">dimensions</span><span class="p">()</span> <span class="o">==</span> <span class="n">dst</span><span class="p">.</span><span class="n">dimensions</span><span class="p">());</span>
|
|
<span class="p">...</span> <span class="c1">// compute the gradient</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p><code class="docutils literal"><span class="pre">gray8c_view_t</span></code> is the type of the source image view - an 8-bit grayscale
|
|
view, whose pixels are read-only (denoted by the “c”).</p>
|
|
<p>The output is a grayscale view with a 8-bit signed (denoted by the “s”)
|
|
integer channel type. See Appendix 1 for the complete convention GIL uses to
|
|
name concrete types.</p>
|
|
<p>GIL makes a distinction between an image and an image view.
|
|
A GIL <strong>image view</strong>, is a shallow, lightweight view of a rectangular grid of
|
|
pixels. It provides access to the pixels but does not own the pixels.
|
|
Copy-constructing a view does not deep-copy the pixels. Image views do not
|
|
propagate their constness to the pixels and should always be taken by a const
|
|
reference. Whether a view is mutable or read-only (immutable) is a property of
|
|
the view type.</p>
|
|
<p>A GIL <cite>image</cite>, on the other hand, is a view with associated ownership.
|
|
It is a container of pixels; its constructor/destructor allocates/deallocates
|
|
the pixels, its copy-constructor performs deep-copy of the pixels and its
|
|
<code class="docutils literal"><span class="pre">operator==</span></code> performs deep-compare of the pixels. Images also propagate
|
|
their constness to their pixels - a constant reference to an image will not
|
|
allow for modifying its pixels.</p>
|
|
<p>Most GIL algorithms operate on image views; images are rarely
|
|
needed. GIL’s design is very similar to that of the STL. The STL
|
|
equivalent of GIL’s image is a container, like <code class="docutils literal"><span class="pre">std::vector</span></code>,
|
|
whereas GIL’s image view corresponds to STL range, which is often
|
|
represented with a pair of iterators. STL algorithms operate on
|
|
ranges, just like GIL algorithms operate on image views.</p>
|
|
<p>GIL’s image views can be constructed from raw data - the dimensions,
|
|
the number of bytes per row and the pixels, which for chunky views are
|
|
represented with one pointer. Here is how to provide the glue between
|
|
your code and GIL:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="kt">void</span> <span class="nf">ComputeXGradientGray8</span><span class="p">(</span>
|
|
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="k">const</span><span class="o">*</span> <span class="n">src_pixels</span><span class="p">,</span> <span class="kt">ptrdiff_t</span> <span class="n">src_row_bytes</span><span class="p">,</span>
|
|
<span class="kt">int</span> <span class="n">w</span><span class="p">,</span> <span class="kt">int</span> <span class="n">h</span><span class="p">,</span>
|
|
<span class="kt">signed</span> <span class="kt">char</span><span class="o">*</span> <span class="n">dst_pixels</span><span class="p">,</span> <span class="kt">ptrdiff_t</span> <span class="n">dst_row_bytes</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">gray8c_view_t</span> <span class="n">src</span> <span class="o">=</span> <span class="n">interleaved_view</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="p">(</span><span class="n">gray8_pixel_t</span> <span class="k">const</span><span class="o">*</span><span class="p">)</span><span class="n">src_pixels</span><span class="p">,</span> <span class="n">src_row_bytes</span><span class="p">);</span>
|
|
<span class="n">gray8s_view_t</span> <span class="n">dst</span> <span class="o">=</span> <span class="n">interleaved_view</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="p">(</span><span class="n">gray8s_pixel_t</span><span class="o">*</span><span class="p">)</span><span class="n">dst_pixels</span><span class="p">,</span> <span class="n">dst_row_bytes</span><span class="p">);</span>
|
|
<span class="n">x_gradient</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">dst</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>This glue code is very fast and views are lightweight - in the above example
|
|
the views have a size of 16 bytes. They consist of a pointer to the top left
|
|
pixel and three integers - the width, height, and number of bytes per row.</p>
|
|
</div>
|
|
<div class="section" id="first-implementation">
|
|
<h2><a class="toc-backref" href="#id2">First Implementation</a></h2>
|
|
<p>Focusing on simplicity at the expense of speed, we can compute the horizontal
|
|
gradient like this:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="kt">void</span> <span class="nf">x_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="n">gray8s_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">y</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">();</span> <span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">x</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
|
<span class="n">dst</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="n">src</span><span class="p">(</span><span class="n">x</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">-</span> <span class="n">src</span><span class="p">(</span><span class="n">x</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">))</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>We use image view’s <code class="docutils literal"><span class="pre">operator(x,y)</span></code> to get a reference to the pixel at a
|
|
given location and we set it to the half-difference of its left and right
|
|
neighbors. <code class="docutils literal"><span class="pre">operator()</span></code> returns a reference to a grayscale pixel.
|
|
A grayscale pixel is convertible to its channel type (<code class="docutils literal"><span class="pre">unsigned</span> <span class="pre">char</span></code> for
|
|
<code class="docutils literal"><span class="pre">src</span></code>) and it can be copy-constructed from a channel.
|
|
(This is only true for grayscale pixels).</p>
|
|
<p>While the above code is easy to read, it is not very fast, because the binary
|
|
<code class="docutils literal"><span class="pre">operator()</span></code> computes the location of the pixel in a 2D grid, which involves
|
|
addition and multiplication. Here is a faster version of the above:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="kt">void</span> <span class="nf">x_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="n">gray8s_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">y</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">();</span> <span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">x_iterator</span> <span class="n">src_it</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">row_begin</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
|
|
<span class="n">gray8s_view_t</span><span class="o">::</span><span class="n">x_iterator</span> <span class="n">dst_it</span> <span class="o">=</span> <span class="n">dst</span><span class="p">.</span><span class="n">row_begin</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
|
|
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">x</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
|
<span class="n">dst_it</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">src_it</span><span class="p">[</span><span class="n">x</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">src_it</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="mi">1</span><span class="p">])</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>We use pixel iterators initialized at the beginning of each row. GIL’s
|
|
iterators are Random Access Traversal iterators. If you are not
|
|
familiar with random access iterators, think of them as if they were
|
|
pointers. In fact, in the above example the two iterator types are raw
|
|
C pointers and their <code class="docutils literal"><span class="pre">operator[]</span></code> is a fast pointer indexing
|
|
operator.</p>
|
|
<p>The code to compute gradient in the vertical direction is very
|
|
similar:</p>
|
|
<p>Instead of looping over the rows, we loop over each column and create a
|
|
<code class="docutils literal"><span class="pre">y_iterator</span></code>, an iterator moving vertically. In this case a simple pointer
|
|
cannot be used because the distance between two adjacent pixels equals the
|
|
number of bytes in each row of the image. GIL uses here a special step
|
|
iterator class whose size is 8 bytes - it contains a raw C pointer and a step.
|
|
Its <code class="docutils literal"><span class="pre">operator[]</span></code> multiplies the index by its step.</p>
|
|
<p>The above version of <code class="docutils literal"><span class="pre">y_gradient</span></code>, however, is much slower (easily an order
|
|
of magnitude slower) than <code class="docutils literal"><span class="pre">x_gradient</span></code> because of the memory access pattern;
|
|
traversing an image vertically results in lots of cache misses. A much more
|
|
efficient and cache-friendly version will iterate over the columns in the inner
|
|
loop:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="kt">void</span> <span class="nf">y_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="n">gray8s_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">y</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">x_iterator</span> <span class="n">src1_it</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">row_begin</span><span class="p">(</span><span class="n">y</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span>
|
|
<span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">x_iterator</span> <span class="n">src2_it</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">row_begin</span><span class="p">(</span><span class="n">y</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span>
|
|
<span class="n">gray8s_view_t</span><span class="o">::</span><span class="n">x_iterator</span> <span class="n">dst_it</span> <span class="o">=</span> <span class="n">dst</span><span class="p">.</span><span class="n">row_begin</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
|
|
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">();</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="o">*</span><span class="n">dst_it</span> <span class="o">=</span> <span class="p">((</span><span class="o">*</span><span class="n">src1_it</span><span class="p">)</span> <span class="o">-</span> <span class="p">(</span><span class="o">*</span><span class="n">src2_it</span><span class="p">))</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
|
|
<span class="o">++</span><span class="n">dst_it</span><span class="p">;</span>
|
|
<span class="o">++</span><span class="n">src1_it</span><span class="p">;</span>
|
|
<span class="o">++</span><span class="n">src2_it</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>This sample code also shows an alternative way of using pixel iterators -
|
|
instead of <code class="docutils literal"><span class="pre">operator[]</span></code> one could use increments and dereferences.</p>
|
|
</div>
|
|
<div class="section" id="using-locators">
|
|
<h2><a class="toc-backref" href="#id3">Using Locators</a></h2>
|
|
<p>Unfortunately this cache-friendly version requires the extra hassle of
|
|
maintaining two separate iterators in the source view. For every pixel, we
|
|
want to access its neighbors above and below it. Such relative access can be
|
|
done with GIL locators:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="kt">void</span> <span class="nf">y_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="n">gray8s_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">xy_locator</span> <span class="n">src_loc</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">xy_at</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span>
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">y</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">gray8s_view_t</span><span class="o">::</span><span class="n">x_iterator</span> <span class="n">dst_it</span> <span class="o">=</span> <span class="n">dst</span><span class="p">.</span><span class="n">row_begin</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
|
|
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">();</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="p">(</span><span class="o">*</span><span class="n">dst_it</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="n">src_loc</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">-</span> <span class="n">src_loc</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">))</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
|
|
<span class="o">++</span><span class="n">dst_it</span><span class="p">;</span>
|
|
<span class="o">++</span><span class="n">src_loc</span><span class="p">.</span><span class="n">x</span><span class="p">();</span> <span class="c1">// each dimension can be advanced separately</span>
|
|
<span class="p">}</span>
|
|
<span class="n">src_loc</span><span class="o">+=</span><span class="n">point</span><span class="o"><</span><span class="n">std</span><span class="o">::</span><span class="kt">ptrdiff_t</span><span class="o">></span><span class="p">(</span><span class="o">-</span><span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">(),</span> <span class="mi">1</span><span class="p">);</span> <span class="c1">// carriage return</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>The first line creates a locator pointing to the first pixel of the
|
|
second row of the source view. A GIL pixel locator is very similar to
|
|
an iterator, except that it can move both horizontally and
|
|
vertically. <code class="docutils literal"><span class="pre">src_loc.x()</span></code> and <code class="docutils literal"><span class="pre">src_loc.y()</span></code> return references to a
|
|
horizontal and a vertical iterator respectively, which can be used to
|
|
move the locator along the desired dimension, as shown
|
|
above. Additionally, the locator can be advanced in both dimensions
|
|
simultaneously using its <code class="docutils literal"><span class="pre">operator+=</span></code> and <code class="docutils literal"><span class="pre">operator-=</span></code>. Similar to
|
|
image views, locators provide binary <code class="docutils literal"><span class="pre">operator()</span></code> which returns a
|
|
reference to a pixel with a relative offset to the current locator
|
|
position. For example, <code class="docutils literal"><span class="pre">src_loc(0,1)</span></code> returns a reference to the
|
|
neighbor below the current pixel. Locators are very lightweight
|
|
objects - in the above example the locator has a size of 8 bytes - it
|
|
consists of a raw pointer to the current pixel and an int indicating
|
|
the number of bytes from one row to the next (which is the step when
|
|
moving vertically). The call to <code class="docutils literal"><span class="pre">++src_loc.x()</span></code> corresponds to a
|
|
single C pointer increment. However, the example above performs more
|
|
computations than necessary. The code <code class="docutils literal"><span class="pre">src_loc(0,1)</span></code> has to compute
|
|
the offset of the pixel in two dimensions, which is slow. Notice
|
|
though that the offset of the two neighbors is the same, regardless of
|
|
the pixel location. To improve the performance, GIL can cache and
|
|
reuse this offset:</p>
|
|
<div class="highlight-c++"><div class="highlight"><pre><span class="kt">void</span> <span class="nf">y_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="n">gray8s_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">xy_locator</span> <span class="n">src_loc</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">xy_at</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span>
|
|
<span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">xy_locator</span><span class="o">::</span><span class="n">cached_location_t</span> <span class="n">above</span> <span class="o">=</span> <span class="n">src_loc</span><span class="p">.</span><span class="n">cache_location</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span>
|
|
<span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">xy_locator</span><span class="o">::</span><span class="n">cached_location_t</span> <span class="n">below</span> <span class="o">=</span> <span class="n">src_loc</span><span class="p">.</span><span class="n">cache_location</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
|
|
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">y</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">gray8s_view_t</span><span class="o">::</span><span class="n">x_iterator</span> <span class="n">dst_it</span> <span class="o">=</span> <span class="n">dst</span><span class="p">.</span><span class="n">row_begin</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
|
|
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">();</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="p">(</span><span class="o">*</span><span class="n">dst_it</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="n">src_loc</span><span class="p">[</span><span class="n">above</span><span class="p">]</span> <span class="o">-</span> <span class="n">src_loc</span><span class="p">[</span><span class="n">below</span><span class="p">])</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
|
|
<span class="o">++</span><span class="n">dst_it</span><span class="p">;</span>
|
|
<span class="o">++</span><span class="n">src_loc</span><span class="p">.</span><span class="n">x</span><span class="p">();</span>
|
|
<span class="p">}</span>
|
|
<span class="n">src_loc</span><span class="o">+=</span><span class="n">point</span><span class="o"><</span><span class="n">std</span><span class="o">::</span><span class="kt">ptrdiff_t</span><span class="o">></span><span class="p">(</span><span class="o">-</span><span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">(),</span> <span class="mi">1</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>In this example <code class="docutils literal"><span class="pre">src_loc[above]</span></code> corresponds to a fast pointer indexing
|
|
operation and the code is efficient.</p>
|
|
</div>
|
|
<div class="section" id="creating-a-generic-version-of-gil-algorithms">
|
|
<h2><a class="toc-backref" href="#id4">Creating a Generic Version of GIL Algorithms</a></h2>
|
|
<p>Let us make our <code class="docutils literal"><span class="pre">x_gradient</span></code> more generic. It should work with any image
|
|
views, as long as they have the same number of channels. The gradient
|
|
operation is to be computed for each channel independently.</p>
|
|
<p>Here is how the new interface looks like:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="k">template</span> <span class="o"><</span><span class="k">typename</span> <span class="n">SrcView</span><span class="p">,</span> <span class="k">typename</span> <span class="n">DstView</span><span class="o">></span>
|
|
<span class="kt">void</span> <span class="n">x_gradient</span><span class="p">(</span><span class="k">const</span> <span class="n">SrcView</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="k">const</span> <span class="n">DstView</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">gil_function_requires</span><span class="o"><</span><span class="n">ImageViewConcept</span><span class="o"><</span><span class="n">SrcView</span><span class="o">></span> <span class="o">></span><span class="p">();</span>
|
|
<span class="n">gil_function_requires</span><span class="o"><</span><span class="n">MutableImageViewConcept</span><span class="o"><</span><span class="n">DstView</span><span class="o">></span> <span class="o">></span><span class="p">();</span>
|
|
<span class="n">gil_function_requires</span>
|
|
<span class="o"><</span>
|
|
<span class="n">ColorSpacesCompatibleConcept</span>
|
|
<span class="o"><</span>
|
|
<span class="k">typename</span> <span class="n">color_space_type</span><span class="o"><</span><span class="n">SrcView</span><span class="o">>::</span><span class="n">type</span><span class="p">,</span>
|
|
<span class="k">typename</span> <span class="n">color_space_type</span><span class="o"><</span><span class="n">DstView</span><span class="o">>::</span><span class="n">type</span>
|
|
<span class="o">></span>
|
|
<span class="o">></span><span class="p">();</span>
|
|
|
|
<span class="p">...</span> <span class="c1">// compute the gradient</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>The new algorithm now takes the types of the input and output image
|
|
views as template parameters. That allows using both built-in GIL
|
|
image views, as well as any user-defined image view classes. The
|
|
first three lines are optional; they use <code class="docutils literal"><span class="pre">boost::concept_check</span></code> to
|
|
ensure that the two arguments are valid GIL image views, that the
|
|
second one is mutable and that their color spaces are compatible
|
|
(i.e. have the same set of channels).</p>
|
|
<p>GIL does not require using its own built-in constructs. You are free
|
|
to use your own channels, color spaces, iterators, locators, views and
|
|
images. However, to work with the rest of GIL they have to satisfy a
|
|
set of requirements; in other words, they have to e model the
|
|
corresponding GIL _concept_. GIL’s concepts are defined in the user
|
|
guide.</p>
|
|
<p>One of the biggest drawbacks of using templates and generic
|
|
programming in C++ is that compile errors can be very difficult to
|
|
comprehend. This is a side-effect of the lack of early type
|
|
checking - a generic argument may not satisfy the requirements of a
|
|
function, but the incompatibility may be triggered deep into a nested
|
|
call, in code unfamiliar and hardly related to the problem. GIL uses
|
|
<code class="docutils literal"><span class="pre">boost::concept_check</span></code> to mitigate this problem. The above three
|
|
lines of code check whether the template parameters are valid models
|
|
of their corresponding concepts. If a model is incorrect, the compile
|
|
error will be inside <code class="docutils literal"><span class="pre">gil_function_requires</span></code>, which is much closer
|
|
to the problem and easier to track. Furthermore, such checks get
|
|
compiled out and have zero performance overhead. The disadvantage of
|
|
using concept checks is the sometimes severe impact they have on
|
|
compile time. This is why GIL performs concept checks only in debug
|
|
mode, and only if <code class="docutils literal"><span class="pre">BOOST_GIL_USE_CONCEPT_CHECK</span></code> is defined (off by
|
|
default).</p>
|
|
<p>The body of the generic function is very similar to that of the
|
|
concrete one. The biggest difference is that we need to loop over the
|
|
channels of the pixel and compute the gradient for each channel:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="k">template</span> <span class="o"><</span><span class="k">typename</span> <span class="n">SrcView</span><span class="p">,</span> <span class="k">typename</span> <span class="n">DstView</span><span class="o">></span>
|
|
<span class="kt">void</span> <span class="n">x_gradient</span><span class="p">(</span><span class="k">const</span> <span class="n">SrcView</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="k">const</span> <span class="n">DstView</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">y</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">();</span> <span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">typename</span> <span class="n">SrcView</span><span class="o">::</span><span class="n">x_iterator</span> <span class="n">src_it</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">row_begin</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
|
|
<span class="k">typename</span> <span class="n">DstView</span><span class="o">::</span><span class="n">x_iterator</span> <span class="n">dst_it</span> <span class="o">=</span> <span class="n">dst</span><span class="p">.</span><span class="n">row_begin</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
|
|
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">x</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">c</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">c</span> <span class="o"><</span> <span class="n">num_channels</span><span class="o"><</span><span class="n">SrcView</span><span class="o">>::</span><span class="n">value</span><span class="p">;</span> <span class="o">++</span><span class="n">c</span><span class="p">)</span>
|
|
<span class="n">dst_it</span><span class="p">[</span><span class="n">x</span><span class="p">][</span><span class="n">c</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">src_it</span><span class="p">[</span><span class="n">x</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">c</span><span class="p">]</span><span class="o">-</span> <span class="n">src_it</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="mi">1</span><span class="p">][</span><span class="n">c</span><span class="p">])</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>Having an explicit loop for each channel could be a performance problem.
|
|
GIL allows us to abstract out such per-channel operations:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="k">template</span> <span class="o"><</span><span class="k">typename</span> <span class="n">Out</span><span class="o">></span>
|
|
<span class="k">struct</span> <span class="n">halfdiff_cast_channels</span>
|
|
<span class="p">{</span>
|
|
<span class="k">template</span> <span class="o"><</span><span class="k">typename</span> <span class="n">T</span><span class="o">></span> <span class="n">Out</span> <span class="k">operator</span><span class="p">()(</span><span class="n">T</span> <span class="k">const</span><span class="o">&</span> <span class="n">in1</span><span class="p">,</span> <span class="n">T</span> <span class="k">const</span><span class="o">&</span> <span class="n">in2</span><span class="p">)</span> <span class="k">const</span>
|
|
<span class="p">{</span>
|
|
<span class="k">return</span> <span class="n">Out</span><span class="p">((</span><span class="n">in1</span> <span class="o">-</span> <span class="n">in2</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="p">};</span>
|
|
|
|
<span class="k">template</span> <span class="o"><</span><span class="k">typename</span> <span class="n">SrcView</span><span class="p">,</span> <span class="k">typename</span> <span class="n">DstView</span><span class="o">></span>
|
|
<span class="kt">void</span> <span class="n">x_gradient</span><span class="p">(</span><span class="k">const</span> <span class="n">SrcView</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="k">const</span> <span class="n">DstView</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">typedef</span> <span class="k">typename</span> <span class="n">channel_type</span><span class="o"><</span><span class="n">DstView</span><span class="o">>::</span><span class="n">type</span> <span class="n">dst_channel_t</span><span class="p">;</span>
|
|
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">y</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">();</span> <span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">typename</span> <span class="n">SrcView</span><span class="o">::</span><span class="n">x_iterator</span> <span class="n">src_it</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">row_begin</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
|
|
<span class="k">typename</span> <span class="n">DstView</span><span class="o">::</span><span class="n">x_iterator</span> <span class="n">dst_it</span> <span class="o">=</span> <span class="n">dst</span><span class="p">.</span><span class="n">row_begin</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
|
|
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">x</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">static_transform</span><span class="p">(</span><span class="n">src_it</span><span class="p">[</span><span class="n">x</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="n">src_it</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="mi">1</span><span class="p">],</span> <span class="n">dst_it</span><span class="p">[</span><span class="n">x</span><span class="p">],</span>
|
|
<span class="n">halfdiff_cast_channels</span><span class="o"><</span><span class="n">dst_channel_t</span><span class="o">></span><span class="p">());</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>The <code class="docutils literal"><span class="pre">static_transform</span></code> is an example of a channel-level GIL algorithm.
|
|
Other such algorithms are <code class="docutils literal"><span class="pre">static_generate</span></code>, <code class="docutils literal"><span class="pre">static_fill</span></code> and
|
|
<code class="docutils literal"><span class="pre">static_for_each</span></code>. They are the channel-level equivalents of STL
|
|
<code class="docutils literal"><span class="pre">generate</span></code>, <code class="docutils literal"><span class="pre">transform</span></code>, <code class="docutils literal"><span class="pre">fill</span></code> and <code class="docutils literal"><span class="pre">for_each</span></code> respectively.
|
|
GIL channel algorithms use static recursion to unroll the loops; they never
|
|
loop over the channels explicitly.</p>
|
|
<p>Note that sometimes modern compilers (at least Visual Studio 8) already unroll
|
|
channel-level loops, such as the one above. However, another advantage of
|
|
using GIL’s channel-level algorithms is that they pair the channels
|
|
semantically, not based on their order in memory. For example, the above
|
|
example will properly match an RGB source with a BGR destination.</p>
|
|
<p>Here is how we can use our generic version with images of different types:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="c1">// Calling with 16-bit grayscale data</span>
|
|
<span class="kt">void</span> <span class="nf">XGradientGray16_Gray32</span><span class="p">(</span>
|
|
<span class="kt">unsigned</span> <span class="kt">short</span> <span class="k">const</span><span class="o">*</span> <span class="n">src_pixels</span><span class="p">,</span> <span class="kt">ptrdiff_t</span> <span class="n">src_row_bytes</span><span class="p">,</span>
|
|
<span class="kt">int</span> <span class="n">w</span><span class="p">,</span> <span class="kt">int</span> <span class="n">h</span><span class="p">,</span>
|
|
<span class="kt">signed</span> <span class="kt">int</span><span class="o">*</span> <span class="n">dst_pixels</span><span class="p">,</span> <span class="kt">ptrdiff_t</span> <span class="n">dst_row_bytes</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">gray16c_view_t</span> <span class="n">src</span><span class="o">=</span><span class="n">interleaved_view</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="p">(</span><span class="n">gray16_pixel_t</span> <span class="k">const</span><span class="o">*</span><span class="p">)</span><span class="n">src_pixels</span><span class="p">,</span> <span class="n">src_row_bytes</span><span class="p">);</span>
|
|
<span class="n">gray32s_view_t</span> <span class="n">dst</span><span class="o">=</span><span class="n">interleaved_view</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="p">(</span><span class="n">gray32s_pixel_t</span><span class="o">*</span><span class="p">)</span><span class="n">dst_pixels</span><span class="p">,</span> <span class="n">dst_row_bytes</span><span class="p">);</span>
|
|
<span class="n">x_gradient</span><span class="p">(</span><span class="n">src</span><span class="p">,</span><span class="n">dst</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="c1">// Calling with 8-bit RGB data into 16-bit BGR</span>
|
|
<span class="kt">void</span> <span class="nf">XGradientRGB8_BGR16</span><span class="p">(</span>
|
|
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="k">const</span><span class="o">*</span> <span class="n">src_pixels</span><span class="p">,</span> <span class="kt">ptrdiff_t</span> <span class="n">src_row_bytes</span><span class="p">,</span>
|
|
<span class="kt">int</span> <span class="n">w</span><span class="p">,</span> <span class="kt">int</span> <span class="n">h</span><span class="p">,</span>
|
|
<span class="kt">signed</span> <span class="kt">short</span><span class="o">*</span> <span class="n">dst_pixels</span><span class="p">,</span> <span class="kt">ptrdiff_t</span> <span class="n">dst_row_bytes</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">rgb8c_view_t</span> <span class="n">src</span> <span class="o">=</span> <span class="n">interleaved_view</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="p">(</span><span class="n">rgb8_pixel_t</span> <span class="k">const</span><span class="o">*</span><span class="p">)</span><span class="n">src_pixels</span><span class="p">,</span> <span class="n">src_row_bytes</span><span class="p">);</span>
|
|
<span class="n">bgr16s_view_t</span> <span class="n">dst</span> <span class="o">=</span> <span class="n">interleaved_view</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="p">(</span><span class="n">bgr16s_pixel_t</span><span class="o">*</span><span class="p">)</span><span class="n">dst_pixels</span><span class="p">,</span> <span class="n">dst_row_bytes</span><span class="p">);</span>
|
|
<span class="n">x_gradient</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">dst</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="c1">// Either or both the source and the destination could be planar - the gradient code does not change</span>
|
|
<span class="kt">void</span> <span class="nf">XGradientPlanarRGB8_RGB32</span><span class="p">(</span>
|
|
<span class="kt">unsigned</span> <span class="kt">short</span> <span class="k">const</span><span class="o">*</span> <span class="n">src_r</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">short</span> <span class="k">const</span><span class="o">*</span> <span class="n">src_g</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">short</span> <span class="k">const</span><span class="o">*</span> <span class="n">src_b</span><span class="p">,</span>
|
|
<span class="kt">ptrdiff_t</span> <span class="n">src_row_bytes</span><span class="p">,</span> <span class="kt">int</span> <span class="n">w</span><span class="p">,</span> <span class="kt">int</span> <span class="n">h</span><span class="p">,</span>
|
|
<span class="kt">signed</span> <span class="kt">int</span><span class="o">*</span> <span class="n">dst_pixels</span><span class="p">,</span> <span class="kt">ptrdiff_t</span> <span class="n">dst_row_bytes</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">rgb16c_planar_view_t</span> <span class="n">src</span> <span class="o">=</span> <span class="n">planar_rgb_view</span> <span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">src_r</span><span class="p">,</span> <span class="n">src_g</span><span class="p">,</span> <span class="n">src_b</span><span class="p">,</span> <span class="n">src_row_bytes</span><span class="p">);</span>
|
|
<span class="n">rgb32s_view_t</span> <span class="n">dst</span> <span class="o">=</span> <span class="n">interleaved_view</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,(</span><span class="n">rgb32s_pixel_t</span><span class="o">*</span><span class="p">)</span><span class="n">dst_pixels</span><span class="p">,</span> <span class="n">dst_row_bytes</span><span class="p">);</span>
|
|
<span class="n">x_gradient</span><span class="p">(</span><span class="n">src</span><span class="p">,</span><span class="n">dst</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>As these examples illustrate, both the source and the destination can be
|
|
interleaved or planar, of any channel depth (assuming the destination channel
|
|
is assignable to the source), and of any compatible color spaces.</p>
|
|
<p>GIL 2.1 can also natively represent images whose channels are not
|
|
byte-aligned, such as 6-bit RGB222 image or a 1-bit Gray1 image.
|
|
GIL algorithms apply to these images natively. See the design guide or sample
|
|
files for more on using such images.</p>
|
|
</div>
|
|
<div class="section" id="image-view-transformations">
|
|
<h2><a class="toc-backref" href="#id5">Image View Transformations</a></h2>
|
|
<p>One way to compute the y-gradient is to rotate the image by 90 degrees,
|
|
compute the x-gradient and rotate the result back.
|
|
Here is how to do this in GIL:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="k">template</span> <span class="o"><</span><span class="k">typename</span> <span class="n">SrcView</span><span class="p">,</span> <span class="k">typename</span> <span class="n">DstView</span><span class="o">></span>
|
|
<span class="kt">void</span> <span class="n">y_gradient</span><span class="p">(</span><span class="k">const</span> <span class="n">SrcView</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="k">const</span> <span class="n">DstView</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">x_gradient</span><span class="p">(</span><span class="n">rotated90ccw_view</span><span class="p">(</span><span class="n">src</span><span class="p">),</span> <span class="n">rotated90ccw_view</span><span class="p">(</span><span class="n">dst</span><span class="p">));</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p><code class="docutils literal"><span class="pre">rotated90ccw_view</span></code> takes an image view and returns an image view
|
|
representing 90-degrees counter-clockwise rotation of its input. It is
|
|
an example of a GIL view transformation function. GIL provides a
|
|
variety of transformation functions that can perform any axis-aligned
|
|
rotation, transpose the view, flip it vertically or horizontally,
|
|
extract a rectangular subimage, perform color conversion, subsample
|
|
view, etc. The view transformation functions are fast and shallow -
|
|
they don’t copy the pixels, they just change the “coordinate system”
|
|
of accessing the pixels. <code class="docutils literal"><span class="pre">rotated90cw_view</span></code>, for example, returns a
|
|
view whose horizontal iterators are the vertical iterators of the
|
|
original view. The above code to compute <code class="docutils literal"><span class="pre">y_gradient</span></code> is slow
|
|
because of the memory access pattern; using <code class="docutils literal"><span class="pre">rotated90cw_view</span></code> does
|
|
not make it any slower.</p>
|
|
<p>Another example: suppose we want to compute the gradient of the N-th
|
|
channel of a color image. Here is how to do that:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="k">template</span> <span class="o"><</span><span class="k">typename</span> <span class="n">SrcView</span><span class="p">,</span> <span class="k">typename</span> <span class="n">DstView</span><span class="o">></span>
|
|
<span class="kt">void</span> <span class="n">nth_channel_x_gradient</span><span class="p">(</span><span class="k">const</span> <span class="n">SrcView</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="k">const</span> <span class="n">DstView</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">x_gradient</span><span class="p">(</span><span class="n">nth_channel_view</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">n</span><span class="p">),</span> <span class="n">dst</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p><code class="docutils literal"><span class="pre">nth_channel_view</span></code> is a view transformation function that takes any
|
|
view and returns a single-channel (grayscale) view of its N-th
|
|
channel. For interleaved RGB view, for example, the returned view is
|
|
a step view - a view whose horizontal iterator skips over two channels
|
|
when incremented. If applied on a planar RGB view, the returned type
|
|
is a simple grayscale view whose horizontal iterator is a C pointer.
|
|
Image view transformation functions can be piped together. For
|
|
example, to compute the y gradient of the second channel of the even
|
|
pixels in the view, use:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="n">y_gradient</span><span class="p">(</span><span class="n">subsampled_view</span><span class="p">(</span><span class="n">nth_channel_view</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">),</span> <span class="n">dst</span><span class="p">);</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>GIL can sometimes simplify piped views. For example, two nested
|
|
subsampled views (views that skip over pixels in X and in Y) can be
|
|
represented as a single subsampled view whose step is the product of
|
|
the steps of the two views.</p>
|
|
</div>
|
|
<div class="section" id="d-pixel-iterators">
|
|
<h2><a class="toc-backref" href="#id6">1D pixel iterators</a></h2>
|
|
<p>Let’s go back to <code class="docutils literal"><span class="pre">x_gradient</span></code> one more time. Many image view
|
|
algorithms apply the same operation for each pixel and GIL provides an
|
|
abstraction to handle them. However, our algorithm has an unusual
|
|
access pattern, as it skips the first and the last column. It would be
|
|
nice and instructional to see how we can rewrite it in canonical
|
|
form. The way to do that in GIL is to write a version that works for
|
|
every pixel, but apply it only on the subimage that excludes the first
|
|
and last column:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="kt">void</span> <span class="nf">x_gradient_unguarded</span><span class="p">(</span><span class="n">gray8c_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="n">gray8s_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">y</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">();</span> <span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">x_iterator</span> <span class="n">src_it</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">row_begin</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
|
|
<span class="n">gray8s_view_t</span><span class="o">::</span><span class="n">x_iterator</span> <span class="n">dst_it</span> <span class="o">=</span> <span class="n">dst</span><span class="p">.</span><span class="n">row_begin</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
|
|
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o"><</span> <span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">();</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
|
<span class="n">dst_it</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">src_it</span><span class="p">[</span><span class="n">x</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">src_it</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="mi">1</span><span class="p">])</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="kt">void</span> <span class="nf">x_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="n">gray8s_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">assert</span><span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">()</span><span class="o">>=</span><span class="mi">2</span><span class="p">);</span>
|
|
<span class="n">x_gradient_unguarded</span><span class="p">(</span><span class="n">subimage_view</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">()</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">()),</span>
|
|
<span class="n">subimage_view</span><span class="p">(</span><span class="n">dst</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">()</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">()));</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p><code class="docutils literal"><span class="pre">subimage_view</span></code> is another example of a GIL view transformation
|
|
function. It takes a source view and a rectangular region (in this
|
|
case, defined as x_min,y_min,width,height) and returns a view
|
|
operating on that region of the source view. The above implementation
|
|
has no measurable performance degradation from the version that
|
|
operates on the original views.</p>
|
|
<p>Now that <code class="docutils literal"><span class="pre">x_gradient_unguarded</span></code> operates on every pixel, we can
|
|
rewrite it more compactly:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="kt">void</span> <span class="nf">x_gradient_unguarded</span><span class="p">(</span><span class="n">gray8c_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="n">gray8s_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">iterator</span> <span class="n">src_it</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span>
|
|
<span class="k">for</span> <span class="p">(</span><span class="n">gray8s_view_t</span><span class="o">::</span><span class="n">iterator</span> <span class="n">dst_it</span> <span class="o">=</span> <span class="n">dst</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> <span class="n">dst_it</span><span class="o">!=</span><span class="n">dst</span><span class="p">.</span><span class="n">end</span><span class="p">();</span> <span class="o">++</span><span class="n">dst_it</span><span class="p">,</span> <span class="o">++</span><span class="n">src_it</span><span class="p">)</span>
|
|
<span class="o">*</span><span class="n">dst_it</span> <span class="o">=</span> <span class="p">(</span><span class="n">src_it</span><span class="p">.</span><span class="n">x</span><span class="p">()[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">src_it</span><span class="p">.</span><span class="n">x</span><span class="p">()[</span><span class="mi">1</span><span class="p">])</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>GIL image views provide <code class="docutils literal"><span class="pre">begin()</span></code> and <code class="docutils literal"><span class="pre">end()</span></code> methods that return
|
|
one dimensional pixel iterators which iterate over each pixel in the
|
|
view, left to right and top to bottom. They do a proper “carriage
|
|
return” - they skip any unused bytes at the end of a row. As such,
|
|
they are slightly suboptimal, because they need to keep track of their
|
|
current position with respect to the end of the row. Their increment
|
|
operator performs one extra check (are we at the end of the row?), a
|
|
check that is avoided if two nested loops are used instead. These
|
|
iterators have a method <code class="docutils literal"><span class="pre">x()</span></code> which returns the more lightweight
|
|
horizontal iterator that we used previously. Horizontal iterators have
|
|
no notion of the end of rows. In this case, the horizontal iterators
|
|
are raw C pointers. In our example, we must use the horizontal
|
|
iterators to access the two neighbors properly, since they could
|
|
reside outside the image view.</p>
|
|
</div>
|
|
<div class="section" id="stl-equivalent-algorithms">
|
|
<h2><a class="toc-backref" href="#id7">STL Equivalent Algorithms</a></h2>
|
|
<p>GIL provides STL equivalents of many algorithms. For example,
|
|
<code class="docutils literal"><span class="pre">std::transform</span></code> is an STL algorithm that sets each element in a
|
|
destination range the result of a generic function taking the
|
|
corresponding element of the source range. In our example, we want to
|
|
assign to each destination pixel the value of the half-difference of
|
|
the horizontal neighbors of the corresponding source pixel. If we
|
|
abstract that operation in a function object, we can use GIL’s
|
|
<code class="docutils literal"><span class="pre">transform_pixel_positions</span></code> to do that:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="k">struct</span> <span class="n">half_x_difference</span>
|
|
<span class="p">{</span>
|
|
<span class="kt">int</span> <span class="k">operator</span><span class="p">()(</span><span class="k">const</span> <span class="n">gray8c_loc_t</span><span class="o">&</span> <span class="n">src_loc</span><span class="p">)</span> <span class="k">const</span>
|
|
<span class="p">{</span>
|
|
<span class="k">return</span> <span class="p">(</span><span class="n">src_loc</span><span class="p">.</span><span class="n">x</span><span class="p">()[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">src_loc</span><span class="p">.</span><span class="n">x</span><span class="p">()[</span><span class="mi">1</span><span class="p">])</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">};</span>
|
|
|
|
<span class="kt">void</span> <span class="nf">x_gradient_unguarded</span><span class="p">(</span><span class="n">gray8c_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="n">gray8s_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">transform_pixel_positions</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">dst</span><span class="p">,</span> <span class="n">half_x_difference</span><span class="p">());</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>GIL provides the algorithms <code class="docutils literal"><span class="pre">for_each_pixel</span></code> and
|
|
<code class="docutils literal"><span class="pre">transform_pixels</span></code> which are image view equivalents of STL
|
|
<code class="docutils literal"><span class="pre">std::for_each</span></code> and <code class="docutils literal"><span class="pre">std::transform</span></code>. It also provides
|
|
<code class="docutils literal"><span class="pre">for_each_pixel_position</span></code> and <code class="docutils literal"><span class="pre">transform_pixel_positions</span></code>, which
|
|
instead of references to pixels, pass to the generic function pixel
|
|
locators. This allows for more powerful functions that can use the
|
|
pixel neighbors through the passed locators. GIL algorithms iterate
|
|
through the pixels using the more efficient two nested loops (as
|
|
opposed to the single loop using 1-D iterators)</p>
|
|
</div>
|
|
<div class="section" id="color-conversion">
|
|
<h2><a class="toc-backref" href="#id8">Color Conversion</a></h2>
|
|
<p>Instead of computing the gradient of each color plane of an image, we
|
|
often want to compute the gradient of the luminosity. In other words,
|
|
we want to convert the color image to grayscale and compute the
|
|
gradient of the result. Here how to compute the luminosity gradient of
|
|
a 32-bit float RGB image:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="kt">void</span> <span class="nf">x_gradient_rgb_luminosity</span><span class="p">(</span><span class="n">rgb32fc_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="n">gray8s_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">x_gradient</span><span class="p">(</span><span class="n">color_converted_view</span><span class="o"><</span><span class="n">gray8_pixel_t</span><span class="o">></span><span class="p">(</span><span class="n">src</span><span class="p">),</span> <span class="n">dst</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p><code class="docutils literal"><span class="pre">color_converted_view</span></code> is a GIL view transformation function that
|
|
takes any image view and returns a view in a target color space and
|
|
channel depth (specified as template parameters). In our example, it
|
|
constructs an 8-bit integer grayscale view over 32-bit float RGB
|
|
pixels. Like all other view transformation functions,
|
|
<code class="docutils literal"><span class="pre">color_converted_view</span></code> is very fast and shallow. It doesn’t copy the
|
|
data or perform any color conversion. Instead it returns a view that
|
|
performs color conversion every time its pixels are accessed.</p>
|
|
<p>In the generic version of this algorithm we might like to convert the
|
|
color space to grayscale, but keep the channel depth the same. We do
|
|
that by constructing the type of a GIL grayscale pixel with the same
|
|
channel as the source, and color convert to that pixel type:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="k">template</span> <span class="o"><</span><span class="k">typename</span> <span class="n">SrcView</span><span class="p">,</span> <span class="k">typename</span> <span class="n">DstView</span><span class="o">></span>
|
|
<span class="kt">void</span> <span class="n">x_luminosity_gradient</span><span class="p">(</span><span class="n">SrcView</span> <span class="k">const</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="n">DstView</span> <span class="k">const</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">using</span> <span class="n">gray_pixel_t</span> <span class="o">=</span> <span class="n">pixel</span><span class="o"><</span><span class="k">typename</span> <span class="n">channel_type</span><span class="o"><</span><span class="n">SrcView</span><span class="o">>::</span><span class="n">type</span><span class="p">,</span> <span class="n">gray_layout_t</span><span class="o">></span><span class="p">;</span>
|
|
<span class="n">x_gradient</span><span class="p">(</span><span class="n">color_converted_view</span><span class="o"><</span><span class="n">gray_pixel_t</span><span class="o">></span><span class="p">(</span><span class="n">src</span><span class="p">),</span> <span class="n">dst</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>When the destination color space and channel type happens to be the
|
|
same as the source one, color conversion is unnecessary. GIL detects
|
|
this case and avoids calling the color conversion code at all -
|
|
i.e. <code class="docutils literal"><span class="pre">color_converted_view</span></code> returns back the source view unchanged.</p>
|
|
</div>
|
|
<div class="section" id="image">
|
|
<h2><a class="toc-backref" href="#id9">Image</a></h2>
|
|
<p>The above example has a performance problem - <code class="docutils literal"><span class="pre">x_gradient</span></code>
|
|
dereferences most source pixels twice, which will cause the above code
|
|
to perform color conversion twice. Sometimes it may be more efficient
|
|
to copy the color converted image into a temporary buffer and use it
|
|
to compute the gradient - that way color conversion is invoked once
|
|
per pixel. Using our non-generic version we can do it like this:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="kt">void</span> <span class="nf">x_luminosity_gradient</span><span class="p">(</span><span class="n">rgb32fc_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="n">gray8s_view_t</span> <span class="k">const</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">gray8_image_t</span> <span class="n">ccv_image</span><span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">dimensions</span><span class="p">());</span>
|
|
<span class="n">copy_pixels</span><span class="p">(</span><span class="n">color_converted_view</span><span class="o"><</span><span class="n">gray8_pixel_t</span><span class="o">></span><span class="p">(</span><span class="n">src</span><span class="p">),</span> <span class="n">view</span><span class="p">(</span><span class="n">ccv_image</span><span class="p">));</span>
|
|
|
|
<span class="n">x_gradient</span><span class="p">(</span><span class="n">const_view</span><span class="p">(</span><span class="n">ccv_image</span><span class="p">),</span> <span class="n">dst</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>First we construct an 8-bit grayscale image with the same dimensions
|
|
as our source. Then we copy a color-converted view of the source into
|
|
the temporary image. Finally we use a read-only view of the temporary
|
|
image in our <code class="docutils literal"><span class="pre">x_gradient</span> <span class="pre">algorithm</span></code>. As the example shows, GIL
|
|
provides global functions <code class="docutils literal"><span class="pre">view</span></code> and <code class="docutils literal"><span class="pre">const_view</span></code> that take an
|
|
image and return a mutable or an immutable view of its pixels.</p>
|
|
<p>Creating a generic version of the above is a bit trickier:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="k">template</span> <span class="o"><</span><span class="k">typename</span> <span class="n">SrcView</span><span class="p">,</span> <span class="k">typename</span> <span class="n">DstView</span><span class="o">></span>
|
|
<span class="kt">void</span> <span class="n">x_luminosity_gradient</span><span class="p">(</span><span class="k">const</span> <span class="n">SrcView</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="k">const</span> <span class="n">DstView</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">using</span> <span class="n">d_channel_t</span> <span class="o">=</span> <span class="k">typename</span> <span class="n">channel_type</span><span class="o"><</span><span class="n">DstView</span><span class="o">>::</span><span class="n">type</span><span class="p">;</span>
|
|
<span class="k">using</span> <span class="n">channel_t</span> <span class="o">=</span> <span class="k">typename</span> <span class="n">channel_convert_to_unsigned</span><span class="o"><</span><span class="n">d_channel_t</span><span class="o">>::</span><span class="n">type</span><span class="p">;</span>
|
|
<span class="k">using</span> <span class="n">gray_pixel_t</span> <span class="o">=</span> <span class="n">pixel</span><span class="o"><</span><span class="n">channel_t</span><span class="p">,</span> <span class="n">gray_layout_t</span><span class="o">></span><span class="p">;</span>
|
|
<span class="k">using</span> <span class="n">gray_image_t</span> <span class="o">=</span> <span class="n">image</span><span class="o"><</span><span class="n">gray_pixel_t</span><span class="p">,</span> <span class="nb">false</span><span class="o">></span><span class="p">;</span>
|
|
|
|
<span class="n">gray_image_t</span> <span class="nf">ccv_image</span><span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">dimensions</span><span class="p">());</span>
|
|
<span class="n">copy_pixels</span><span class="p">(</span><span class="n">color_converted_view</span><span class="o"><</span><span class="n">gray_pixel_t</span><span class="o">></span><span class="p">(</span><span class="n">src</span><span class="p">),</span> <span class="n">view</span><span class="p">(</span><span class="n">ccv_image</span><span class="p">));</span>
|
|
<span class="n">x_gradient</span><span class="p">(</span><span class="n">const_view</span><span class="p">(</span><span class="n">ccv_image</span><span class="p">),</span> <span class="n">dst</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>First we use the <code class="docutils literal"><span class="pre">channel_type</span></code> metafunction to get the channel type
|
|
of the destination view. A metafunction is a function operating on
|
|
types. In GIL metafunctions are class templates (declared with
|
|
<code class="docutils literal"><span class="pre">struct</span></code> type specifier) which take their parameters as template
|
|
parameters and return their result in a nested typedef called
|
|
<code class="docutils literal"><span class="pre">type</span></code>. In this case, <code class="docutils literal"><span class="pre">channel_type</span></code> is a unary metafunction which
|
|
in this example is called with the type of an image view and returns
|
|
the type of the channel associated with that image view.</p>
|
|
<p>GIL constructs that have an associated pixel type, such as pixels,
|
|
pixel iterators, locators, views and images, all model
|
|
<code class="docutils literal"><span class="pre">PixelBasedConcept</span></code>, which means that they provide a set of
|
|
metafunctions to query the pixel properties, such as <code class="docutils literal"><span class="pre">channel_type</span></code>,
|
|
<code class="docutils literal"><span class="pre">color_space_type</span></code>, <code class="docutils literal"><span class="pre">channel_mapping_type</span></code>, and <code class="docutils literal"><span class="pre">num_channels</span></code>.</p>
|
|
<p>After we get the channel type of the destination view, we use another
|
|
metafunction to remove its sign (if it is a signed integral type) and
|
|
then use it to generate the type of a grayscale pixel. From the pixel
|
|
type we create the image type. GIL’s image class is specialized over
|
|
the pixel type and a boolean indicating whether the image should be
|
|
planar or interleaved. Single-channel (grayscale) images in GIL must
|
|
always be interleaved. There are multiple ways of constructing types
|
|
in GIL. Instead of instantiating the classes directly we could have
|
|
used type factory metafunctions. The following code is equivalent:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="k">template</span> <span class="o"><</span><span class="k">typename</span> <span class="n">SrcView</span><span class="p">,</span> <span class="k">typename</span> <span class="n">DstView</span><span class="o">></span>
|
|
<span class="kt">void</span> <span class="n">x_luminosity_gradient</span><span class="p">(</span><span class="n">SrcView</span> <span class="k">const</span><span class="o">&</span> <span class="n">src</span><span class="p">,</span> <span class="n">DstView</span> <span class="k">const</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">typedef</span> <span class="k">typename</span> <span class="n">channel_type</span><span class="o"><</span><span class="n">DstView</span><span class="o">>::</span><span class="n">type</span> <span class="n">d_channel_t</span><span class="p">;</span>
|
|
<span class="k">typedef</span> <span class="k">typename</span> <span class="n">channel_convert_to_unsigned</span><span class="o"><</span><span class="n">d_channel_t</span><span class="o">>::</span><span class="n">type</span> <span class="n">channel_t</span><span class="p">;</span>
|
|
<span class="k">typedef</span> <span class="k">typename</span> <span class="n">image_type</span><span class="o"><</span><span class="n">channel_t</span><span class="p">,</span> <span class="n">gray_layout_t</span><span class="o">>::</span><span class="n">type</span> <span class="n">gray_image_t</span><span class="p">;</span>
|
|
<span class="k">typedef</span> <span class="k">typename</span> <span class="n">gray_image_t</span><span class="o">::</span><span class="n">value_type</span> <span class="n">gray_pixel_t</span><span class="p">;</span>
|
|
|
|
<span class="n">gray_image_t</span> <span class="nf">ccv_image</span><span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">dimensions</span><span class="p">());</span>
|
|
<span class="n">copy_and_convert_pixels</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">view</span><span class="p">(</span><span class="n">ccv_image</span><span class="p">));</span>
|
|
<span class="n">x_gradient</span><span class="p">(</span><span class="n">const_view</span><span class="p">(</span><span class="n">ccv_image</span><span class="p">),</span> <span class="n">dst</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>GIL provides a set of metafunctions that generate GIL types -
|
|
<code class="docutils literal"><span class="pre">image_type</span></code> is one such meta-function that constructs the type of
|
|
an image from a given channel type, color layout, and
|
|
planar/interleaved option (the default is interleaved). There are also
|
|
similar meta-functions to construct the types of pixel references,
|
|
iterators, locators and image views. GIL also has metafunctions
|
|
<code class="docutils literal"><span class="pre">derived_pixel_reference_type</span></code>, <code class="docutils literal"><span class="pre">derived_iterator_type</span></code>,
|
|
<code class="docutils literal"><span class="pre">derived_view_type</span></code> and <code class="docutils literal"><span class="pre">derived_image_type</span></code> that construct the
|
|
type of a GIL construct from a given source one by changing one or
|
|
more properties of the type and keeping the rest.</p>
|
|
<p>From the image type we can use the nested typedef <code class="docutils literal"><span class="pre">value_type</span></code> to
|
|
obtain the type of a pixel. GIL images, image views and locators have
|
|
nested typedefs <code class="docutils literal"><span class="pre">value_type</span></code> and <code class="docutils literal"><span class="pre">reference</span></code> to obtain the type of
|
|
the pixel and a reference to the pixel. If you have a pixel iterator,
|
|
you can get these types from its <code class="docutils literal"><span class="pre">iterator_traits</span></code>. Note also the
|
|
algorithm <code class="docutils literal"><span class="pre">copy_and_convert_pixels</span></code>, which is an abbreviated version
|
|
of <code class="docutils literal"><span class="pre">copy_pixels</span></code> with a color converted source view.</p>
|
|
</div>
|
|
<div class="section" id="virtual-image-views">
|
|
<h2><a class="toc-backref" href="#id10">Virtual Image Views</a></h2>
|
|
<p>So far we have been dealing with images that have pixels stored in
|
|
memory. GIL allows you to create an image view of an arbitrary image,
|
|
including a synthetic function. To demonstrate this, let us create a
|
|
view of the Mandelbrot set. First, we need to create a function
|
|
object that computes the value of the Mandelbrot set at a given
|
|
location (x,y) in the image:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="c1">// models PixelDereferenceAdaptorConcept</span>
|
|
<span class="k">struct</span> <span class="n">mandelbrot_fn</span>
|
|
<span class="p">{</span>
|
|
<span class="k">typedef</span> <span class="n">point</span><span class="o"><</span><span class="kt">ptrdiff_t</span><span class="o">></span> <span class="n">point_t</span><span class="p">;</span>
|
|
|
|
<span class="k">typedef</span> <span class="n">mandelbrot_fn</span> <span class="n">const_t</span><span class="p">;</span>
|
|
<span class="k">typedef</span> <span class="n">gray8_pixel_t</span> <span class="n">value_type</span><span class="p">;</span>
|
|
<span class="k">typedef</span> <span class="n">value_type</span> <span class="n">reference</span><span class="p">;</span>
|
|
<span class="k">typedef</span> <span class="n">value_type</span> <span class="n">const_reference</span><span class="p">;</span>
|
|
<span class="k">typedef</span> <span class="n">point_t</span> <span class="n">argument_type</span><span class="p">;</span>
|
|
<span class="k">typedef</span> <span class="n">reference</span> <span class="n">result_type</span><span class="p">;</span>
|
|
<span class="k">static</span> <span class="kt">bool</span> <span class="k">constexpr</span> <span class="n">is_mutable</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
|
|
|
|
<span class="n">mandelbrot_fn</span><span class="p">()</span> <span class="p">{}</span>
|
|
<span class="n">mandelbrot_fn</span><span class="p">(</span><span class="k">const</span> <span class="n">point_t</span><span class="o">&</span> <span class="n">sz</span><span class="p">)</span> <span class="o">:</span> <span class="n">_img_size</span><span class="p">(</span><span class="n">sz</span><span class="p">)</span> <span class="p">{}</span>
|
|
|
|
<span class="n">result_type</span> <span class="k">operator</span><span class="p">()(</span><span class="k">const</span> <span class="n">point_t</span><span class="o">&</span> <span class="n">p</span><span class="p">)</span> <span class="k">const</span>
|
|
<span class="p">{</span>
|
|
<span class="c1">// normalize the coords to (-2..1, -1.5..1.5)</span>
|
|
<span class="kt">double</span> <span class="n">t</span><span class="o">=</span><span class="n">get_num_iter</span><span class="p">(</span><span class="n">point</span><span class="o"><</span><span class="kt">double</span><span class="o">></span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">x</span><span class="o">/</span><span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">_img_size</span><span class="p">.</span><span class="n">x</span><span class="o">*</span><span class="mi">3</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">y</span><span class="o">/</span><span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">_img_size</span><span class="p">.</span><span class="n">y</span><span class="o">*</span><span class="mi">3</span><span class="o">-</span><span class="mf">1.5f</span><span class="p">));</span>
|
|
<span class="k">return</span> <span class="nf">value_type</span><span class="p">((</span><span class="n">bits8</span><span class="p">)(</span><span class="n">pow</span><span class="p">(</span><span class="n">t</span><span class="p">,</span><span class="mf">0.2</span><span class="p">)</span><span class="o">*</span><span class="mi">255</span><span class="p">));</span> <span class="c1">// raise to power suitable for viewing</span>
|
|
<span class="p">}</span>
|
|
<span class="k">private</span><span class="o">:</span>
|
|
<span class="n">point_t</span> <span class="n">_img_size</span><span class="p">;</span>
|
|
|
|
<span class="kt">double</span> <span class="nf">get_num_iter</span><span class="p">(</span><span class="k">const</span> <span class="n">point</span><span class="o"><</span><span class="kt">double</span><span class="o">>&</span> <span class="n">p</span><span class="p">)</span> <span class="k">const</span>
|
|
<span class="p">{</span>
|
|
<span class="n">point</span><span class="o"><</span><span class="kt">double</span><span class="o">></span> <span class="n">Z</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
|
|
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o"><</span><span class="mi">100</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="c1">// 100 iterations</span>
|
|
<span class="p">{</span>
|
|
<span class="n">Z</span> <span class="o">=</span> <span class="n">point</span><span class="o"><</span><span class="kt">double</span><span class="o">></span><span class="p">(</span><span class="n">Z</span><span class="p">.</span><span class="n">x</span><span class="o">*</span><span class="n">Z</span><span class="p">.</span><span class="n">x</span> <span class="o">-</span> <span class="n">Z</span><span class="p">.</span><span class="n">y</span><span class="o">*</span><span class="n">Z</span><span class="p">.</span><span class="n">y</span> <span class="o">+</span> <span class="n">p</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="n">Z</span><span class="p">.</span><span class="n">x</span><span class="o">*</span><span class="n">Z</span><span class="p">.</span><span class="n">y</span> <span class="o">+</span> <span class="n">p</span><span class="p">.</span><span class="n">y</span><span class="p">);</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">Z</span><span class="p">.</span><span class="n">x</span><span class="o">*</span><span class="n">Z</span><span class="p">.</span><span class="n">x</span> <span class="o">+</span> <span class="n">Z</span><span class="p">.</span><span class="n">y</span><span class="o">*</span><span class="n">Z</span><span class="p">.</span><span class="n">y</span> <span class="o">></span> <span class="mi">4</span><span class="p">)</span>
|
|
<span class="k">return</span> <span class="n">i</span><span class="o">/</span><span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="mi">100</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">};</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>We can now use GIL’s <code class="docutils literal"><span class="pre">virtual_2d_locator</span></code> with this function object
|
|
to construct a Mandelbrot view of size 200x200 pixels:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="k">typedef</span> <span class="n">mandelbrot_fn</span><span class="o">::</span><span class="n">point_t</span> <span class="n">point_t</span><span class="p">;</span>
|
|
<span class="k">typedef</span> <span class="n">virtual_2d_locator</span><span class="o"><</span><span class="n">mandelbrot_fn</span><span class="p">,</span><span class="nb">false</span><span class="o">></span> <span class="n">locator_t</span><span class="p">;</span>
|
|
<span class="k">typedef</span> <span class="n">image_view</span><span class="o"><</span><span class="n">locator_t</span><span class="o">></span> <span class="n">my_virt_view_t</span><span class="p">;</span>
|
|
|
|
<span class="n">point_t</span> <span class="nf">dims</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span><span class="mi">200</span><span class="p">);</span>
|
|
|
|
<span class="c1">// Construct a Mandelbrot view with a locator, taking top-left corner (0,0) and step (1,1)</span>
|
|
<span class="n">my_virt_view_t</span> <span class="nf">mandel</span><span class="p">(</span><span class="n">dims</span><span class="p">,</span> <span class="n">locator_t</span><span class="p">(</span><span class="n">point_t</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">),</span> <span class="n">point_t</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">),</span> <span class="n">mandelbrot_fn</span><span class="p">(</span><span class="n">dims</span><span class="p">)));</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>We can treat the synthetic view just like a real one. For example,
|
|
let’s invoke our <code class="docutils literal"><span class="pre">x_gradient</span></code> algorithm to compute the gradient of
|
|
the 90-degree rotated view of the Mandelbrot set and save the original
|
|
and the result:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="n">gray8s_image_t</span> <span class="nf">img</span><span class="p">(</span><span class="n">dims</span><span class="p">);</span>
|
|
<span class="n">x_gradient</span><span class="p">(</span><span class="n">rotated90cw_view</span><span class="p">(</span><span class="n">mandel</span><span class="p">),</span> <span class="n">view</span><span class="p">(</span><span class="n">img</span><span class="p">));</span>
|
|
|
|
<span class="c1">// Save the Mandelbrot set and its 90-degree rotated gradient (jpeg cannot save signed char; must convert to unsigned char)</span>
|
|
<span class="n">jpeg_write_view</span><span class="p">(</span><span class="s">"mandel.jpg"</span><span class="p">,</span><span class="n">mandel</span><span class="p">);</span>
|
|
<span class="n">jpeg_write_view</span><span class="p">(</span><span class="s">"mandel_grad.jpg"</span><span class="p">,</span><span class="n">color_converted_view</span><span class="o"><</span><span class="n">gray8_pixel_t</span><span class="o">></span><span class="p">(</span><span class="n">const_view</span><span class="p">(</span><span class="n">img</span><span class="p">)));</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>Here is what the two files look like:</p>
|
|
<img alt="../_images/mandel.jpg" src="../_images/mandel.jpg" />
|
|
</div>
|
|
<div class="section" id="run-time-specified-images-and-image-views">
|
|
<h2><a class="toc-backref" href="#id11">Run-Time Specified Images and Image Views</a></h2>
|
|
<p>So far we have created a generic function that computes the image
|
|
gradient of an image view template specialization. Sometimes,
|
|
however, the properties of an image view, such as its color space and
|
|
channel depth, may not be available at compile time. GIL’s
|
|
<code class="docutils literal"><span class="pre">dynamic_image</span></code> extension allows for working with GIL constructs
|
|
that are specified at run time, also called _variants_. GIL provides
|
|
models of a run-time instantiated image, <code class="docutils literal"><span class="pre">any_image</span></code>, and a run-time
|
|
instantiated image view, <code class="docutils literal"><span class="pre">any_image_view</span></code>. The mechanisms are in
|
|
place to create other variants, such as <code class="docutils literal"><span class="pre">any_pixel</span></code>,
|
|
<code class="docutils literal"><span class="pre">any_pixel_iterator</span></code>, etc. Most of GIL’s algorithms and all of the
|
|
view transformation functions also work with run-time instantiated
|
|
image views and binary algorithms, such as <code class="docutils literal"><span class="pre">copy_pixels</span></code> can have
|
|
either or both arguments be variants.</p>
|
|
<p>Lets make our <code class="docutils literal"><span class="pre">x_luminosity_gradient</span></code> algorithm take a variant image
|
|
view. For simplicity, let’s assume that only the source view can be a
|
|
variant. (As an example of using multiple variants, see GIL’s image
|
|
view algorithm overloads taking multiple variants.)</p>
|
|
<p>First, we need to make a function object that contains the templated
|
|
destination view and has an application operator taking a templated
|
|
source view:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="cp">#include</span> <span class="cpf"><boost/gil/extension/dynamic_image/dynamic_image_all.hpp></span><span class="cp"></span>
|
|
|
|
<span class="k">template</span> <span class="o"><</span><span class="k">typename</span> <span class="n">DstView</span><span class="o">></span>
|
|
<span class="k">struct</span> <span class="n">x_gradient_obj</span>
|
|
<span class="p">{</span>
|
|
<span class="k">typedef</span> <span class="kt">void</span> <span class="n">result_type</span><span class="p">;</span> <span class="c1">// required typedef</span>
|
|
|
|
<span class="k">const</span> <span class="n">DstView</span><span class="o">&</span> <span class="n">_dst</span><span class="p">;</span>
|
|
<span class="n">x_gradient_obj</span><span class="p">(</span><span class="k">const</span> <span class="n">DstView</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span> <span class="o">:</span> <span class="n">_dst</span><span class="p">(</span><span class="n">dst</span><span class="p">)</span> <span class="p">{}</span>
|
|
|
|
<span class="k">template</span> <span class="o"><</span><span class="k">typename</span> <span class="n">SrcView</span><span class="o">></span>
|
|
<span class="kt">void</span> <span class="k">operator</span><span class="p">()(</span><span class="k">const</span> <span class="n">SrcView</span><span class="o">&</span> <span class="n">src</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> <span class="n">x_luminosity_gradient</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">_dst</span><span class="p">);</span> <span class="p">}</span>
|
|
<span class="p">};</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>The second step is to provide an overload of <code class="docutils literal"><span class="pre">x_luminosity_gradient</span></code> that
|
|
takes image view variant and calls GIL’s <code class="docutils literal"><span class="pre">apply_operation</span></code> passing it the
|
|
function object:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="k">template</span> <span class="o"><</span><span class="k">typename</span> <span class="n">SrcViews</span><span class="p">,</span> <span class="k">typename</span> <span class="n">DstView</span><span class="o">></span>
|
|
<span class="kt">void</span> <span class="n">x_luminosity_gradient</span><span class="p">(</span><span class="k">const</span> <span class="n">any_image_view</span><span class="o"><</span><span class="n">SrcViews</span><span class="o">>&</span> <span class="n">src</span><span class="p">,</span> <span class="k">const</span> <span class="n">DstView</span><span class="o">&</span> <span class="n">dst</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">apply_operation</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">x_gradient_obj</span><span class="o"><</span><span class="n">DstView</span><span class="o">></span><span class="p">(</span><span class="n">dst</span><span class="p">));</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p><code class="docutils literal"><span class="pre">any_image_view<SrcViews></span></code> is the image view variant. It is
|
|
templated over <code class="docutils literal"><span class="pre">SrcViews</span></code>, an enumeration of all possible view types
|
|
the variant can take. <code class="docutils literal"><span class="pre">src</span></code> contains inside an index of the
|
|
currently instantiated type, as well as a block of memory containing
|
|
the instance. <code class="docutils literal"><span class="pre">apply_operation</span></code> goes through a switch statement
|
|
over the index, each case of which casts the memory to the correct
|
|
view type and invokes the function object with it. Invoking an
|
|
algorithm on a variant has the overhead of one switch
|
|
statement. Algorithms that perform an operation for each pixel in an
|
|
image view have practically no performance degradation when used with
|
|
a variant.</p>
|
|
<p>Here is how we can construct a variant and invoke the algorithm:</p>
|
|
<div class="highlight-cpp"><div class="highlight"><pre><span class="cp">#include</span> <span class="cpf"><boost/mp11.hpp></span><span class="cp"></span>
|
|
<span class="cp">#include</span> <span class="cpf"><boost/gil/extension/io/jpeg/old.hpp></span><span class="cp"></span>
|
|
|
|
<span class="k">typedef</span> <span class="n">mp11</span><span class="o">::</span><span class="n">mp_list</span><span class="o"><</span><span class="n">gray8_image_t</span><span class="p">,</span> <span class="n">gray16_image_t</span><span class="p">,</span> <span class="n">rgb8_image_t</span><span class="p">,</span> <span class="n">rgb16_image_t</span><span class="o">></span> <span class="n">my_img_types</span><span class="p">;</span>
|
|
<span class="n">any_image</span><span class="o"><</span><span class="n">my_img_types</span><span class="o">></span> <span class="n">runtime_image</span><span class="p">;</span>
|
|
<span class="n">jpeg_read_image</span><span class="p">(</span><span class="s">"input.jpg"</span><span class="p">,</span> <span class="n">runtime_image</span><span class="p">);</span>
|
|
|
|
<span class="n">gray8s_image_t</span> <span class="nf">gradient</span><span class="p">(</span><span class="n">runtime_image</span><span class="p">.</span><span class="n">dimensions</span><span class="p">());</span>
|
|
<span class="n">x_luminosity_gradient</span><span class="p">(</span><span class="n">const_view</span><span class="p">(</span><span class="n">runtime_image</span><span class="p">),</span> <span class="n">view</span><span class="p">(</span><span class="n">gradient</span><span class="p">));</span>
|
|
<span class="n">jpeg_write_view</span><span class="p">(</span><span class="s">"x_gradient.jpg"</span><span class="p">,</span> <span class="n">color_converted_view</span><span class="o"><</span><span class="n">gray8_pixel_t</span><span class="o">></span><span class="p">(</span><span class="n">const_view</span><span class="p">(</span><span class="n">gradient</span><span class="p">)));</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>In this example, we create an image variant that could be 8-bit or
|
|
16-bit RGB or grayscale image. We then use GIL’s I/O extension to load
|
|
the image from file in its native color space and channel depth. If
|
|
none of the allowed image types matches the image on disk, an
|
|
exception will be thrown. We then construct a 8 bit signed
|
|
(i.e. <code class="docutils literal"><span class="pre">char</span></code>) image to store the gradient and invoke <code class="docutils literal"><span class="pre">x_gradient</span></code>
|
|
on it. Finally we save the result into another file. We save the view
|
|
converted to 8-bit unsigned, because JPEG I/O does not support signed
|
|
char.</p>
|
|
<p>Note how free functions and methods such as <code class="docutils literal"><span class="pre">jpeg_read_image</span></code>,
|
|
<code class="docutils literal"><span class="pre">dimensions</span></code>, <code class="docutils literal"><span class="pre">view</span></code> and <code class="docutils literal"><span class="pre">const_view</span></code> work on both templated and
|
|
variant types. For templated images <code class="docutils literal"><span class="pre">view(img)</span></code> returns a templated
|
|
view, whereas for image variants it returns a view variant. For
|
|
example, the return type of <code class="docutils literal"><span class="pre">view(runtime_image)</span></code> is
|
|
<code class="docutils literal"><span class="pre">any_image_view<Views></span></code> where <code class="docutils literal"><span class="pre">Views</span></code> enumerates four views
|
|
corresponding to the four image types. <code class="docutils literal"><span class="pre">const_view(runtime_image)</span></code>
|
|
returns a <code class="docutils literal"><span class="pre">any_image_view</span></code> of the four read-only view types, etc.</p>
|
|
<p>A warning about using variants: instantiating an algorithm with a
|
|
variant effectively instantiates it with every possible type the
|
|
variant can take. For binary algorithms, the algorithm is
|
|
instantiated with every possible combination of the two input types!
|
|
This can take a toll on both the compile time and the executable size.</p>
|
|
</div>
|
|
<div class="section" id="conclusion">
|
|
<h2><a class="toc-backref" href="#id12">Conclusion</a></h2>
|
|
<p>This tutorial provides a glimpse at the challenges associated with
|
|
writing generic and efficient image processing algorithms in GIL. We
|
|
have taken a simple algorithm and shown how to make it work with image
|
|
representations that vary in bit depth, color space, ordering of the
|
|
channels, and planar/interleaved structure. We have demonstrated that
|
|
the algorithm can work with fully abstracted virtual images, and even
|
|
images whose type is specified at run time. The associated video
|
|
presentation also demonstrates that even for complex scenarios the
|
|
generated assembly is comparable to that of a C version of the
|
|
algorithm, hand-written for the specific image types.</p>
|
|
<p>Yet, even for such a simple algorithm, we are far from making a fully
|
|
generic and optimized code. In particular, the presented algorithms
|
|
work on homogeneous images, i.e. images whose pixels have channels
|
|
that are all of the same type. There are examples of images, such as a
|
|
packed 565 RGB format, which contain channels of different
|
|
types. While GIL provides concepts and algorithms operating on
|
|
heterogeneous pixels, we leave the task of extending x_gradient as an
|
|
exercise for the reader. Second, after computing the value of the
|
|
gradient we are simply casting it to the destination channel
|
|
type. This may not always be the desired operation. For example, if
|
|
the source channel is a float with range [0..1] and the destination is
|
|
unsigned char, casting the half-difference to unsigned char will
|
|
result in either 0 or 1. Instead, what we might want to do is scale
|
|
the result into the range of the destination channel. GIL’s
|
|
channel-level algorithms might be useful in such cases. For example,
|
|
p channel_convert converts between channels by linearly scaling the
|
|
source channel value into the range of the destination channel.</p>
|
|
<p>There is a lot to be done in improving the performance as
|
|
well. Channel-level operations, such as the half-difference, could be
|
|
abstracted out into atomic channel-level algorithms and performance
|
|
overloads could be provided for concrete channel
|
|
types. Processor-specific operations could be used, for example, to
|
|
perform the operation over an entire row of pixels simultaneously, or
|
|
the data could be pre-fetched. All of these optimizations can be
|
|
realized as performance specializations of the generic
|
|
algorithm. Finally, compilers, while getting better over time, are
|
|
still failing to fully optimize generic code in some cases, such as
|
|
failing to inline some functions or put some variables into
|
|
registers. If performance is an issue, it might be worth trying your
|
|
code with different compilers.</p>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="navbar" style="text-align:right;">
|
|
|
|
|
|
<a class="prev" title="Tutorial: Histogram" href="histogram.html"><img src="../_static/prev.png" alt="prev"/></a>
|
|
<a class="next" title="Naming Conventions" href="../naming.html"><img src="../_static/next.png" alt="next"/></a>
|
|
|
|
</div>
|
|
</div>
|
|
<div class="footer" role="contentinfo">
|
|
Last updated on 2021-04-13 16:04:40.
|
|
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.5.6.
|
|
</div>
|
|
</body>
|
|
</html> |