mirror of
https://github.com/boostorg/gil.git
synced 2026-01-19 16:22:14 +00:00
997 lines
142 KiB
HTML
997 lines
142 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" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
|
||
<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/documentation_options.js"></script>
|
||
<script type="text/javascript" src="../_static/doctools.js"></script>
|
||
<script type="text/javascript" src="../_static/sphinx_highlight.js"></script>
|
||
<script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
|
||
<script src="../_static/searchtools.js"></script>
|
||
<script src="../_static/language_data.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>
|
||
|
||
<section id="tutorial-image-gradient">
|
||
<h1>Tutorial: Image Gradient<a class="headerlink" href="#tutorial-image-gradient" title="Link to this heading">¶</a></h1>
|
||
<nav class="contents local" id="contents">
|
||
<ul class="simple">
|
||
<li><p><a class="reference internal" href="#interface-and-glue-code" id="id1">Interface and Glue Code</a></p></li>
|
||
<li><p><a class="reference internal" href="#first-implementation" id="id2">First Implementation</a></p></li>
|
||
<li><p><a class="reference internal" href="#using-locators" id="id3">Using Locators</a></p></li>
|
||
<li><p><a class="reference internal" href="#creating-a-generic-version-of-gil-algorithms" id="id4">Creating a Generic Version of GIL Algorithms</a></p></li>
|
||
<li><p><a class="reference internal" href="#image-view-transformations" id="id5">Image View Transformations</a></p></li>
|
||
<li><p><a class="reference internal" href="#d-pixel-iterators" id="id6">1D pixel iterators</a></p></li>
|
||
<li><p><a class="reference internal" href="#stl-equivalent-algorithms" id="id7">STL Equivalent Algorithms</a></p></li>
|
||
<li><p><a class="reference internal" href="#color-conversion" id="id8">Color Conversion</a></p></li>
|
||
<li><p><a class="reference internal" href="#image" id="id9">Image</a></p></li>
|
||
<li><p><a class="reference internal" href="#virtual-image-views" id="id10">Virtual Image Views</a></p></li>
|
||
<li><p><a class="reference internal" href="#run-time-specified-images-and-image-views" id="id11">Run-Time Specified Images and Image Views</a></p></li>
|
||
<li><p><a class="reference internal" href="#conclusion" id="id12">Conclusion</a></p></li>
|
||
</ul>
|
||
</nav>
|
||
<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-cpp notranslate"><div class="highlight"><pre><span></span><span class="n">D</span><span class="p">[</span><span class="n">x</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">I</span><span class="p">[</span><span class="n">x</span><span class="mi">-1</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </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="w"> </span><span class="o">/</span><span class="w"> </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>
|
||
<section id="interface-and-glue-code">
|
||
<h2><a class="toc-backref" href="#id1" role="doc-backlink">Interface and Glue Code</a><a class="headerlink" href="#interface-and-glue-code" title="Link to this heading">¶</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 notranslate"><div class="highlight"><pre><span></span><span class="cp">#include</span><span class="w"> </span><span class="cpf"><boost/gil.hpp></span>
|
||
|
||
<span class="k">using</span><span class="w"> </span><span class="k">namespace</span><span class="w"> </span><span class="nn">boost</span><span class="o">::</span><span class="nn">gil</span><span class="p">;</span>
|
||
|
||
<span class="kt">void</span><span class="w"> </span><span class="nf">x_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">gray8s_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </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="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">dst</span><span class="p">.</span><span class="n">dimensions</span><span class="p">());</span>
|
||
<span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="c1">// compute the gradient</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><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 an 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 <em>image view</em> 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 <em>image</em>, 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 notranslate"><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 notranslate"><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 notranslate"><div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">ComputeXGradientGray8</span><span class="p">(</span>
|
||
<span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="k">const</span><span class="o">*</span><span class="w"> </span><span class="n">src_pixels</span><span class="p">,</span><span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="kt">ptrdiff_t</span><span class="w"> </span><span class="n">src_row_bytes</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">h</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="kt">signed</span><span class="w"> </span><span class="kt">char</span><span class="o">*</span><span class="w"> </span><span class="n">dst_pixels</span><span class="p">,</span><span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="kt">ptrdiff_t</span><span class="w"> </span><span class="n">dst_row_bytes</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">gray8c_view_t</span><span class="w"> </span><span class="n">src</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">interleaved_view</span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="n">h</span><span class="p">,</span><span class="w"> </span><span class="k">reinterpret_cast</span><span class="o"><</span><span class="n">gray8_pixel_t</span><span class="w"> </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="w"> </span><span class="n">src_row_bytes</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">gray8s_view_t</span><span class="w"> </span><span class="n">dst</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">interleaved_view</span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="n">h</span><span class="p">,</span><span class="w"> </span><span class="k">reinterpret_cast</span><span class="o"><</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="w"> </span><span class="n">dst_row_bytes</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">x_gradient</span><span class="p">(</span><span class="n">src</span><span class="p">,</span><span class="w"> </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>
|
||
</section>
|
||
<section id="first-implementation">
|
||
<h2><a class="toc-backref" href="#id2" role="doc-backlink">First Implementation</a><a class="headerlink" href="#first-implementation" title="Link to this heading">¶</a></h2>
|
||
<p>Focusing on simplicity at the expense of speed, we can compute the horizontal
|
||
gradient like this:</p>
|
||
<div class="highlight-cpp notranslate"><div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">x_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">gray8s_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">();</span><span class="w"> </span><span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">()</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="n">dst</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">src</span><span class="p">(</span><span class="n">x</span><span class="mi">-1</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </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="w"> </span><span class="n">y</span><span class="p">))</span><span class="w"> </span><span class="o">/</span><span class="w"> </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 notranslate"><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 notranslate"><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 notranslate"><span class="pre">unsigned</span> <span class="pre">char</span></code> for
|
||
<code class="docutils literal notranslate"><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 notranslate"><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 notranslate"><div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">x_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">gray8s_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">();</span><span class="w"> </span><span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">x_iterator</span><span class="w"> </span><span class="n">src_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="n">gray8s_view_t</span><span class="o">::</span><span class="n">x_iterator</span><span class="w"> </span><span class="n">dst_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">()</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="n">dst_it</span><span class="p">[</span><span class="n">x</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">src_it</span><span class="p">[</span><span class="n">x</span><span class="mi">-1</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </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="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span>
|
||
<span class="w"> </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 notranslate"><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>
|
||
<div class="highlight-cpp notranslate"><div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">y_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">gray8s_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">();</span><span class="w"> </span><span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">y_iterator</span><span class="w"> </span><span class="n">src_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">col_begin</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">gray8s_view_t</span><span class="o">::</span><span class="n">y_iterator</span><span class="w"> </span><span class="n">dst_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dst</span><span class="p">.</span><span class="n">col_begin</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
|
||
|
||
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">()</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="n">dst_it</span><span class="p">[</span><span class="n">y</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">src_it</span><span class="p">[</span><span class="n">y</span><span class="mi">-1</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">src_it</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="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Instead of looping over the rows, we loop over each column and create a
|
||
<code class="docutils literal notranslate"><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 twice the size of the iterator in horizontal
|
||
direction - it contains a raw C pointer and a step. Its <code class="docutils literal notranslate"><span class="pre">operator[]</span></code> multiplies
|
||
the index by its step.</p>
|
||
<p>The above version of <code class="docutils literal notranslate"><span class="pre">y_gradient</span></code>, however, is much slower (easily an order
|
||
of magnitude slower) than <code class="docutils literal notranslate"><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 notranslate"><div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">y_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">gray8s_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">()</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">x_iterator</span><span class="w"> </span><span class="n">src1_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="mi">-1</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">x_iterator</span><span class="w"> </span><span class="n">src2_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="n">gray8s_view_t</span><span class="o">::</span><span class="n">x_iterator</span><span class="w"> </span><span class="n">dst_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">();</span><span class="w"> </span><span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="o">*</span><span class="n">dst_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">((</span><span class="o">*</span><span class="n">src1_it</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="n">src2_it</span><span class="p">))</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="o">++</span><span class="n">dst_it</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="o">++</span><span class="n">src1_it</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="o">++</span><span class="n">src2_it</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </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 notranslate"><span class="pre">operator[]</span></code> one could use increments and dereferences.</p>
|
||
</section>
|
||
<section id="using-locators">
|
||
<h2><a class="toc-backref" href="#id3" role="doc-backlink">Using Locators</a><a class="headerlink" href="#using-locators" title="Link to this heading">¶</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 notranslate"><div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">y_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">gray8s_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">xy_locator</span><span class="w"> </span><span class="n">src_loc</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">()</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">gray8s_view_t</span><span class="o">::</span><span class="n">x_iterator</span><span class="w"> </span><span class="n">dst_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">();</span><span class="w"> </span><span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="o">*</span><span class="n">dst_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="mi">-1</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </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="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="o">++</span><span class="n">dst_it</span><span class="p">;</span>
|
||
<span class="w"> </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="w"> </span><span class="c1">// each dimension can be advanced separately</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="n">src_loc</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">point_t</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="w"> </span><span class="mi">1</span><span class="p">);</span><span class="w"> </span><span class="c1">// carriage return</span>
|
||
<span class="w"> </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 notranslate"><span class="pre">src_loc.x()</span></code> and <code class="docutils literal notranslate"><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 notranslate"><span class="pre">operator+=</span></code> and <code class="docutils literal notranslate"><span class="pre">operator-=</span></code>. Similar to
|
||
image views, locators provide binary <code class="docutils literal notranslate"><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 notranslate"><span class="pre">src_loc(0,1)</span></code> returns a reference to the
|
||
neighbor below the current pixel. Locators are very lightweight
|
||
objects - they consist 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 notranslate"><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 notranslate"><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-cpp notranslate"><div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">y_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">gray8s_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">xy_locator</span><span class="w"> </span><span class="n">src_loc</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </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="w"> </span><span class="n">above</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </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="w"> </span><span class="n">below</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="mi">1</span><span class="p">);</span>
|
||
|
||
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">()</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">gray8s_view_t</span><span class="o">::</span><span class="n">x_iterator</span><span class="w"> </span><span class="n">dst_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">();</span><span class="w"> </span><span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="o">*</span><span class="n">dst_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">src_loc</span><span class="p">[</span><span class="n">below</span><span class="p">])</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="o">++</span><span class="n">dst_it</span><span class="p">;</span>
|
||
<span class="w"> </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="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="n">src_loc</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">point_t</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="w"> </span><span class="mi">1</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In this example <code class="docutils literal notranslate"><span class="pre">src_loc[above]</span></code> corresponds to a fast pointer indexing
|
||
operation and the code is efficient.</p>
|
||
</section>
|
||
<section id="creating-a-generic-version-of-gil-algorithms">
|
||
<h2><a class="toc-backref" href="#id4" role="doc-backlink">Creating a Generic Version of GIL Algorithms</a><a class="headerlink" href="#creating-a-generic-version-of-gil-algorithms" title="Link to this heading">¶</a></h2>
|
||
<p>Let us make our <code class="docutils literal notranslate"><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 notranslate"><div class="highlight"><pre><span></span><span class="k">template</span><span class="w"> </span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="nc">SrcView</span><span class="p">,</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">DstView</span><span class="o">></span>
|
||
<span class="kt">void</span><span class="w"> </span><span class="n">x_gradient</span><span class="p">(</span><span class="n">SrcView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">DstView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </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="p">();</span>
|
||
<span class="w"> </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="p">();</span>
|
||
<span class="w"> </span><span class="n">gil_function_requires</span>
|
||
<span class="w"> </span><span class="o"><</span>
|
||
<span class="w"> </span><span class="n">ColorSpacesCompatibleConcept</span>
|
||
<span class="w"> </span><span class="o"><</span>
|
||
<span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">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="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">color_space_type</span><span class="o"><</span><span class="n">DstView</span><span class="o">>::</span><span class="n">type</span>
|
||
<span class="w"> </span><span class="o">></span>
|
||
<span class="w"> </span><span class="o">></span><span class="p">();</span>
|
||
|
||
<span class="w"> </span><span class="p">...</span><span class="w"> </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 notranslate"><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 emph model the
|
||
corresponding GIL <em>concept</em>. 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 notranslate"><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 notranslate"><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 notranslate"><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 notranslate"><div class="highlight"><pre><span></span><span class="k">template</span><span class="w"> </span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="nc">SrcView</span><span class="p">,</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">DstView</span><span class="o">></span>
|
||
<span class="kt">void</span><span class="w"> </span><span class="n">x_gradient</span><span class="p">(</span><span class="n">SrcView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">DstView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">();</span><span class="w"> </span><span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">SrcView</span><span class="o">::</span><span class="n">x_iterator</span><span class="w"> </span><span class="n">src_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">DstView</span><span class="o">::</span><span class="n">x_iterator</span><span class="w"> </span><span class="n">dst_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">()</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="kt">size_t</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o"><</span><span class="w"> </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="w"> </span><span class="o">++</span><span class="n">c</span><span class="p">)</span>
|
||
<span class="w"> </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="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">src_it</span><span class="p">[</span><span class="n">x</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="w"> </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="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span>
|
||
<span class="w"> </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 notranslate"><div class="highlight"><pre><span></span><span class="k">template</span><span class="w"> </span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="nc">Out</span><span class="o">></span>
|
||
<span class="k">struct</span><span class="w"> </span><span class="nc">halfdiff_cast_channels</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">template</span><span class="w"> </span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="nc">T</span><span class="o">></span>
|
||
<span class="w"> </span><span class="k">auto</span><span class="w"> </span><span class="k">operator</span><span class="p">()(</span><span class="n">T</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">in1</span><span class="p">,</span><span class="w"> </span><span class="n">T</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">in2</span><span class="p">)</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="o">-></span><span class="w"> </span><span class="n">Out</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">Out</span><span class="p">((</span><span class="n">in1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">in2</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">};</span>
|
||
|
||
<span class="k">template</span><span class="w"> </span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="nc">SrcView</span><span class="p">,</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">DstView</span><span class="o">></span>
|
||
<span class="kt">void</span><span class="w"> </span><span class="n">x_gradient</span><span class="p">(</span><span class="n">SrcView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">DstView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">dst_channel_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">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="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">();</span><span class="w"> </span><span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">SrcView</span><span class="o">::</span><span class="n">x_iterator</span><span class="w"> </span><span class="n">src_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">DstView</span><span class="o">::</span><span class="n">x_iterator</span><span class="w"> </span><span class="n">dst_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">()</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </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="mi">-1</span><span class="p">],</span><span class="w"> </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="w"> </span><span class="n">dst_it</span><span class="p">[</span><span class="n">x</span><span class="p">],</span>
|
||
<span class="w"> </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="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">static_transform</span></code> is an example of a channel-level GIL algorithm.
|
||
Other such algorithms are <code class="docutils literal notranslate"><span class="pre">static_generate</span></code>, <code class="docutils literal notranslate"><span class="pre">static_fill</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">static_for_each</span></code>. They are the channel-level equivalents of STL
|
||
<code class="docutils literal notranslate"><span class="pre">generate</span></code>, <code class="docutils literal notranslate"><span class="pre">transform</span></code>, <code class="docutils literal notranslate"><span class="pre">fill</span></code> and <code class="docutils literal notranslate"><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 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 notranslate"><div class="highlight"><pre><span></span><span class="c1">// Calling with 16-bit grayscale data</span>
|
||
<span class="kt">void</span><span class="w"> </span><span class="nf">XGradientGray16_Gray32</span><span class="p">(</span>
|
||
<span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="kt">short</span><span class="w"> </span><span class="k">const</span><span class="o">*</span><span class="w"> </span><span class="n">src_pixels</span><span class="p">,</span><span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="kt">ptrdiff_t</span><span class="w"> </span><span class="n">src_row_bytes</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">h</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="kt">signed</span><span class="w"> </span><span class="kt">int</span><span class="o">*</span><span class="w"> </span><span class="n">dst_pixels</span><span class="p">,</span><span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="kt">ptrdiff_t</span><span class="w"> </span><span class="n">dst_row_bytes</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">gray16c_view_t</span><span class="w"> </span><span class="n">src</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">interleaved_view</span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="n">h</span><span class="p">,</span><span class="w"> </span><span class="k">reinterpret_cast</span><span class="o"><</span><span class="n">gray16_pixel_t</span><span class="w"> </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="w"> </span><span class="n">src_row_bytes</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">gray32s_view_t</span><span class="w"> </span><span class="n">dst</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">interleaved_view</span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="n">h</span><span class="p">,</span><span class="w"> </span><span class="k">reinterpret_cast</span><span class="o"><</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="w"> </span><span class="n">dst_row_bytes</span><span class="p">);</span>
|
||
<span class="w"> </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="w"> </span><span class="nf">XGradientRGB8_BGR16</span><span class="p">(</span>
|
||
<span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="k">const</span><span class="o">*</span><span class="w"> </span><span class="n">src_pixels</span><span class="p">,</span><span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="kt">ptrdiff_t</span><span class="w"> </span><span class="n">src_row_bytes</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">h</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="kt">signed</span><span class="w"> </span><span class="kt">short</span><span class="o">*</span><span class="w"> </span><span class="n">dst_pixels</span><span class="p">,</span><span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="kt">ptrdiff_t</span><span class="w"> </span><span class="n">dst_row_bytes</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">rgb8c_view_t</span><span class="w"> </span><span class="n">src</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">interleaved_view</span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="n">h</span><span class="p">,</span><span class="w"> </span><span class="k">reinterpret_cast</span><span class="o"><</span><span class="n">rgb8_pixel_t</span><span class="w"> </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="w"> </span><span class="n">src_row_bytes</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">bgr16s_view_t</span><span class="w"> </span><span class="n">dst</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">interleaved_view</span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="n">h</span><span class="p">,</span><span class="w"> </span><span class="k">reinterpret_cast</span><span class="o"><</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="w"> </span><span class="n">dst_row_bytes</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">x_gradient</span><span class="p">(</span><span class="n">src</span><span class="p">,</span><span class="w"> </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="w"> </span><span class="nf">XGradientPlanarRGB8_RGB32</span><span class="p">(</span>
|
||
<span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="kt">short</span><span class="w"> </span><span class="k">const</span><span class="o">*</span><span class="w"> </span><span class="n">src_r</span><span class="p">,</span><span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="kt">short</span><span class="w"> </span><span class="k">const</span><span class="o">*</span><span class="w"> </span><span class="n">src_g</span><span class="p">,</span><span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="kt">short</span><span class="w"> </span><span class="k">const</span><span class="o">*</span><span class="w"> </span><span class="n">src_b</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="kt">ptrdiff_t</span><span class="w"> </span><span class="n">src_row_bytes</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">h</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="kt">signed</span><span class="w"> </span><span class="kt">int</span><span class="o">*</span><span class="w"> </span><span class="n">dst_pixels</span><span class="p">,</span><span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="kt">ptrdiff_t</span><span class="w"> </span><span class="n">dst_row_bytes</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">rgb16c_planar_view_t</span><span class="w"> </span><span class="n">src</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">planar_rgb_view</span><span class="w"> </span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="n">h</span><span class="p">,</span><span class="w"> </span><span class="n">src_r</span><span class="p">,</span><span class="w"> </span><span class="n">src_g</span><span class="p">,</span><span class="w"> </span><span class="n">src_b</span><span class="p">,</span><span class="w"> </span><span class="n">src_row_bytes</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">rgb32s_view_t</span><span class="w"> </span><span class="n">dst</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">interleaved_view</span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="n">h</span><span class="p">,</span><span class="w"> </span><span class="k">reinterpret_cast</span><span class="o"><</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="w"> </span><span class="n">dst_row_bytes</span><span class="p">);</span>
|
||
<span class="w"> </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>
|
||
</section>
|
||
<section id="image-view-transformations">
|
||
<h2><a class="toc-backref" href="#id5" role="doc-backlink">Image View Transformations</a><a class="headerlink" href="#image-view-transformations" title="Link to this heading">¶</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 notranslate"><div class="highlight"><pre><span></span><span class="k">template</span><span class="w"> </span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="nc">SrcView</span><span class="p">,</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">DstView</span><span class="o">></span>
|
||
<span class="kt">void</span><span class="w"> </span><span class="n">y_gradient</span><span class="p">(</span><span class="n">SrcView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">DstView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </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="w"> </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 notranslate"><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 notranslate"><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 notranslate"><span class="pre">y_gradient</span></code> is slow
|
||
because of the memory access pattern; using <code class="docutils literal notranslate"><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 notranslate"><div class="highlight"><pre><span></span><span class="k">template</span><span class="w"> </span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="nc">SrcView</span><span class="p">,</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">DstView</span><span class="o">></span>
|
||
<span class="kt">void</span><span class="w"> </span><span class="n">nth_channel_x_gradient</span><span class="p">(</span><span class="n">SrcView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="n">DstView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </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="w"> </span><span class="n">n</span><span class="p">),</span><span class="w"> </span><span class="n">dst</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><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 notranslate"><div class="highlight"><pre><span></span><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="w"> </span><span class="mi">1</span><span class="p">),</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">),</span><span class="w"> </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>
|
||
</section>
|
||
<section id="d-pixel-iterators">
|
||
<h2><a class="toc-backref" href="#id6" role="doc-backlink">1D pixel iterators</a><a class="headerlink" href="#d-pixel-iterators" title="Link to this heading">¶</a></h2>
|
||
<p>Let’s go back to <code class="docutils literal notranslate"><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 notranslate"><div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">x_gradient_unguarded</span><span class="p">(</span><span class="n">gray8c_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">gray8s_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">();</span><span class="w"> </span><span class="o">++</span><span class="n">y</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">x_iterator</span><span class="w"> </span><span class="n">src_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="n">gray8s_view_t</span><span class="o">::</span><span class="n">x_iterator</span><span class="w"> </span><span class="n">dst_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">();</span><span class="w"> </span><span class="o">++</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="n">dst_it</span><span class="p">[</span><span class="n">x</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">src_it</span><span class="p">[</span><span class="n">x</span><span class="mi">-1</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </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="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kt">void</span><span class="w"> </span><span class="nf">x_gradient</span><span class="p">(</span><span class="n">gray8c_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">gray8s_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </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="w"> </span><span class="o">>=</span><span class="w"> </span><span class="mi">2</span><span class="p">);</span>
|
||
<span class="w"> </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="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">()</span><span class="mi">-2</span><span class="p">,</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">height</span><span class="p">()),</span>
|
||
<span class="w"> </span><span class="n">subimage_view</span><span class="p">(</span><span class="n">dst</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">width</span><span class="p">()</span><span class="mi">-2</span><span class="p">,</span><span class="w"> </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 notranslate"><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 notranslate"><span class="pre">x_gradient_unguarded</span></code> operates on every pixel, we can
|
||
rewrite it more compactly:</p>
|
||
<div class="highlight-cpp notranslate"><div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">x_gradient_unguarded</span><span class="p">(</span><span class="n">gray8c_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">gray8s_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">gray8c_view_t</span><span class="o">::</span><span class="n">iterator</span><span class="w"> </span><span class="n">src_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">src</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span>
|
||
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">gray8s_view_t</span><span class="o">::</span><span class="n">iterator</span><span class="w"> </span><span class="n">dst_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dst</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span><span class="w"> </span><span class="n">dst_it</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">dst</span><span class="p">.</span><span class="n">end</span><span class="p">();</span><span class="w"> </span><span class="o">++</span><span class="n">dst_it</span><span class="p">,</span><span class="w"> </span><span class="o">++</span><span class="n">src_it</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="o">*</span><span class="n">dst_it</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="mi">-1</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </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="w"> </span><span class="o">/</span><span class="w"> </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 notranslate"><span class="pre">begin()</span></code> and <code class="docutils literal notranslate"><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 notranslate"><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>
|
||
</section>
|
||
<section id="stl-equivalent-algorithms">
|
||
<h2><a class="toc-backref" href="#id7" role="doc-backlink">STL Equivalent Algorithms</a><a class="headerlink" href="#stl-equivalent-algorithms" title="Link to this heading">¶</a></h2>
|
||
<p>GIL provides STL equivalents of many algorithms. For example,
|
||
<code class="docutils literal notranslate"><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 notranslate"><span class="pre">transform_pixel_positions</span></code> to do that:</p>
|
||
<div class="highlight-cpp notranslate"><div class="highlight"><pre><span></span><span class="k">struct</span><span class="w"> </span><span class="nc">half_x_difference</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">auto</span><span class="w"> </span><span class="k">operator</span><span class="p">()(</span><span class="n">gray8c_loc_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src_loc</span><span class="p">)</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="o">-></span><span class="w"> </span><span class="kt">int</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </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="mi">-1</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </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="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">};</span>
|
||
|
||
<span class="kt">void</span><span class="w"> </span><span class="nf">x_gradient_unguarded</span><span class="p">(</span><span class="n">gray8c_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">gray8s_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">transform_pixel_positions</span><span class="p">(</span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">dst</span><span class="p">,</span><span class="w"> </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 notranslate"><span class="pre">for_each_pixel</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">transform_pixels</span></code> which are image view equivalents of STL
|
||
<code class="docutils literal notranslate"><span class="pre">std::for_each</span></code> and <code class="docutils literal notranslate"><span class="pre">std::transform</span></code>. It also provides
|
||
<code class="docutils literal notranslate"><span class="pre">for_each_pixel_position</span></code> and <code class="docutils literal notranslate"><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>
|
||
</section>
|
||
<section id="color-conversion">
|
||
<h2><a class="toc-backref" href="#id8" role="doc-backlink">Color Conversion</a><a class="headerlink" href="#color-conversion" title="Link to this heading">¶</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 is how to compute the luminosity gradient of
|
||
a 32-bit float RGB image:</p>
|
||
<div class="highlight-cpp notranslate"><div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">x_gradient_rgb_luminosity</span><span class="p">(</span><span class="n">rgb32fc_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">gray8s_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </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="w"> </span><span class="n">dst</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><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 notranslate"><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 notranslate"><div class="highlight"><pre><span></span><span class="k">template</span><span class="w"> </span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="nc">SrcView</span><span class="p">,</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">DstView</span><span class="o">></span>
|
||
<span class="kt">void</span><span class="w"> </span><span class="n">x_luminosity_gradient</span><span class="p">(</span><span class="n">SrcView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">DstView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">gray_pixel_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pixel</span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="nc">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="w"> </span><span class="n">gray_layout_t</span><span class="o">></span><span class="p">;</span>
|
||
<span class="w"> </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="w"> </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 notranslate"><span class="pre">color_converted_view</span></code> returns back the source view unchanged.</p>
|
||
</section>
|
||
<section id="image">
|
||
<h2><a class="toc-backref" href="#id9" role="doc-backlink">Image</a><a class="headerlink" href="#image" title="Link to this heading">¶</a></h2>
|
||
<p>The above example has a performance problem - <code class="docutils literal notranslate"><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 notranslate"><div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">x_luminosity_gradient</span><span class="p">(</span><span class="n">rgb32fc_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">gray8s_view_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">gray8_image_t</span><span class="w"> </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="w"> </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="w"> </span><span class="n">view</span><span class="p">(</span><span class="n">ccv_image</span><span class="p">));</span>
|
||
|
||
<span class="w"> </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="w"> </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 notranslate"><span class="pre">x_gradient</span> <span class="pre">algorithm</span></code>. As the example shows, GIL
|
||
provides global functions <code class="docutils literal notranslate"><span class="pre">view</span></code> and <code class="docutils literal notranslate"><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 notranslate"><div class="highlight"><pre><span></span><span class="k">template</span><span class="w"> </span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="nc">SrcView</span><span class="p">,</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">DstView</span><span class="o">></span>
|
||
<span class="kt">void</span><span class="w"> </span><span class="n">x_luminosity_gradient</span><span class="p">(</span><span class="n">SrcView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">DstView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">d_channel_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">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="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">channel_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">detail</span><span class="o">::</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="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">gray_pixel_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pixel</span><span class="o"><</span><span class="n">channel_t</span><span class="p">,</span><span class="w"> </span><span class="n">gray_layout_t</span><span class="o">></span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">gray_image_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">image</span><span class="o"><</span><span class="n">gray_pixel_t</span><span class="p">,</span><span class="w"> </span><span class="nb">false</span><span class="o">></span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="n">gray_image_t</span><span class="w"> </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="w"> </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="w"> </span><span class="n">view</span><span class="p">(</span><span class="n">ccv_image</span><span class="p">));</span>
|
||
<span class="w"> </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="w"> </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 notranslate"><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 notranslate"><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 notranslate"><span class="pre">type</span></code>. In this case, <code class="docutils literal notranslate"><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 notranslate"><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 notranslate"><span class="pre">channel_type</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">color_space_type</span></code>, <code class="docutils literal notranslate"><span class="pre">channel_mapping_type</span></code>, and <code class="docutils literal notranslate"><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 notranslate"><div class="highlight"><pre><span></span><span class="k">template</span><span class="w"> </span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="nc">SrcView</span><span class="p">,</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">DstView</span><span class="o">></span>
|
||
<span class="kt">void</span><span class="w"> </span><span class="n">x_luminosity_gradient</span><span class="p">(</span><span class="n">SrcView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">DstView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">d_channel_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">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="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">channel_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">detail</span><span class="o">::</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="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">gray_image_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">image_type</span><span class="o"><</span><span class="n">channel_t</span><span class="p">,</span><span class="w"> </span><span class="n">gray_layout_t</span><span class="o">>::</span><span class="n">type</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="n">gray_image_t</span><span class="w"> </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="w"> </span><span class="n">copy_and_convert_pixels</span><span class="p">(</span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">view</span><span class="p">(</span><span class="n">ccv_image</span><span class="p">));</span>
|
||
<span class="w"> </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="w"> </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 notranslate"><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 notranslate"><span class="pre">derived_pixel_reference_type</span></code>, <code class="docutils literal notranslate"><span class="pre">derived_iterator_type</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">derived_view_type</span></code> and <code class="docutils literal notranslate"><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 notranslate"><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 notranslate"><span class="pre">value_type</span></code> and <code class="docutils literal notranslate"><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 notranslate"><span class="pre">iterator_traits</span></code>. Note also the
|
||
algorithm <code class="docutils literal notranslate"><span class="pre">copy_and_convert_pixels</span></code>, which is an abbreviated version
|
||
of <code class="docutils literal notranslate"><span class="pre">copy_pixels</span></code> with a color converted source view.</p>
|
||
</section>
|
||
<section id="virtual-image-views">
|
||
<h2><a class="toc-backref" href="#id10" role="doc-backlink">Virtual Image Views</a><a class="headerlink" href="#virtual-image-views" title="Link to this heading">¶</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 notranslate"><div class="highlight"><pre><span></span><span class="c1">// models PixelDereferenceAdaptorConcept</span>
|
||
<span class="k">template</span><span class="w"> </span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="nc">Pixel</span><span class="o">></span><span class="w"> </span><span class="c1">// Models PixelValueConcept</span>
|
||
<span class="k">struct</span><span class="w"> </span><span class="nc">mandelbrot_fn</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">point_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">point</span><span class="o"><</span><span class="kt">ptrdiff_t</span><span class="o">></span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">const_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mandelbrot_fn</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">value_type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Pixel</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">reference</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">value_type</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">const_reference</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">value_type</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">argument_type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">point_t</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">result_type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">reference</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">static</span><span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="k">constexpr</span><span class="w"> </span><span class="n">is_mutable</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">false</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="n">mandelbrot_fn</span><span class="p">()</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">default</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="n">mandelbrot_fn</span><span class="p">(</span><span class="n">point_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">sz</span><span class="p">,</span><span class="w"> </span><span class="n">value_type</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">in_color</span><span class="p">,</span><span class="w"> </span><span class="n">value_type</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">out_color</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="n">_in_color</span><span class="p">(</span><span class="n">in_color</span><span class="p">),</span><span class="w"> </span><span class="n">_out_color</span><span class="p">(</span><span class="n">out_color</span><span class="p">),</span><span class="w"> </span><span class="n">_img_size</span><span class="p">(</span><span class="n">sz</span><span class="p">)</span><span class="w"> </span><span class="p">{}</span>
|
||
|
||
<span class="w"> </span><span class="k">auto</span><span class="w"> </span><span class="k">operator</span><span class="p">()(</span><span class="n">point_t</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">p</span><span class="p">)</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="o">-></span><span class="w"> </span><span class="n">result_type</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="c1">// normalize the coords to (-2..1, -1.5..1.5)</span>
|
||
<span class="w"> </span><span class="c1">// (actually make y -1.0..2 so it is asymmetric, so we can verify some view factory methods)</span>
|
||
<span class="w"> </span><span class="kt">double</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="o">=</span><span class="w"> </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-2</span><span class="p">,</span><span class="w"> </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="mf">-1.0f</span><span class="p">));</span><span class="c1">//1.5f));</span>
|
||
<span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="n">result_type</span><span class="w"> </span><span class="n">ret</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="kt">size_t</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">k</span><span class="o"><</span><span class="n">num_channels</span><span class="o"><</span><span class="n">Pixel</span><span class="o">>::</span><span class="n">value</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">k</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="n">ret</span><span class="p">[</span><span class="n">k</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">channel_type</span><span class="o"><</span><span class="n">Pixel</span><span class="o">>::</span><span class="n">type</span><span class="p">(</span><span class="n">_in_color</span><span class="p">[</span><span class="n">k</span><span class="p">]</span><span class="o">*</span><span class="n">t</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">_out_color</span><span class="p">[</span><span class="n">k</span><span class="p">]</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span><span class="o">-</span><span class="n">t</span><span class="p">));</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">ret</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
|
||
<span class="k">private</span><span class="o">:</span>
|
||
<span class="w"> </span><span class="n">value_type</span><span class="w"> </span><span class="n">_in_color</span><span class="p">,</span><span class="n">_out_color</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="n">point_t</span><span class="w"> </span><span class="n">_img_size</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">static</span><span class="w"> </span><span class="k">constexpr</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">MAX_ITER</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">100</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="k">auto</span><span class="w"> </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="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">p</span><span class="p">)</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="o">-></span><span class="w"> </span><span class="kt">double</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">point</span><span class="o"><</span><span class="kt">double</span><span class="o">></span><span class="w"> </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="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">MAX_ITER</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">i</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">Z</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="o">-</span><span class="w"> </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="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">p</span><span class="p">.</span><span class="n">x</span><span class="p">,</span><span class="w"> </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="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">p</span><span class="p">.</span><span class="n">y</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </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="w"> </span><span class="o">+</span><span class="w"> </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="w"> </span><span class="o">></span><span class="w"> </span><span class="mi">4</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">i</span><span class="o">/</span><span class="k">static_cast</span><span class="o"><</span><span class="kt">double</span><span class="o">></span><span class="p">(</span><span class="n">MAX_ITER</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">};</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We can now use GIL’s <code class="docutils literal notranslate"><span class="pre">virtual_2d_locator</span></code> with this function object
|
||
to construct a rgb8 Mandelbrot view of size 200x200 pixels:</p>
|
||
<div class="highlight-cpp notranslate"><div class="highlight"><pre><span></span><span class="k">using</span><span class="w"> </span><span class="n">deref_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mandelbrot_fn</span><span class="o"><</span><span class="n">rgb8_pixel_t</span><span class="o">></span><span class="p">;</span>
|
||
<span class="k">using</span><span class="w"> </span><span class="n">point_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">deref_t</span><span class="o">::</span><span class="n">point_t</span><span class="p">;</span>
|
||
<span class="k">using</span><span class="w"> </span><span class="n">locator_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">virtual_2d_locator</span><span class="o"><</span><span class="n">deref_t</span><span class="p">,</span><span class="w"> </span><span class="nb">false</span><span class="o">></span><span class="p">;</span>
|
||
<span class="k">using</span><span class="w"> </span><span class="n">my_virt_view_t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">image_view</span><span class="o"><</span><span class="n">locator_t</span><span class="o">></span><span class="p">;</span>
|
||
|
||
<span class="n">point_t</span><span class="w"> </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="w"> </span><span class="nf">mandel</span><span class="p">(</span><span class="n">dims</span><span class="p">,</span><span class="w"> </span><span class="n">locator_t</span><span class="p">(</span>
|
||
<span class="w"> </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="w"> </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="w"> </span><span class="n">deref_t</span><span class="p">(</span><span class="n">dims</span><span class="p">,</span><span class="w"> </span><span class="n">rgb8_pixel_t</span><span class="p">(</span><span class="mi">255</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">255</span><span class="p">),</span><span class="w"> </span><span class="n">rgb8_pixel_t</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">255</span><span class="p">,</span><span class="w"> </span><span class="mi">0</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 notranslate"><span class="pre">x_luminosity_gradient</span></code> algorithm to compute the luminosity gradient of
|
||
the 90-degree rotated view of the Mandelbrot set and save the original
|
||
and the result:</p>
|
||
<div class="highlight-cpp notranslate"><div class="highlight"><pre><span></span><span class="cp">#include</span><span class="w"> </span><span class="cpf"><boost/gil/extension/io/jpeg.hpp></span>
|
||
|
||
<span class="n">gray8s_image_t</span><span class="w"> </span><span class="nf">img</span><span class="p">(</span><span class="n">dims</span><span class="p">);</span>
|
||
<span class="n">x_luminosity_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="w"> </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</span>
|
||
<span class="c1">// (jpeg cannot save signed char; must convert to unsigned char)</span>
|
||
<span class="n">write_view</span><span class="p">(</span><span class="s">"mandel.jpg"</span><span class="p">,</span><span class="w"> </span><span class="n">mandel</span><span class="p">,</span><span class="w"> </span><span class="n">jpeg_tag</span><span class="p">{});</span>
|
||
<span class="n">write_view</span><span class="p">(</span><span class="s">"mandel_grad.jpg"</span><span class="p">,</span><span class="w"> </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><span class="w"> </span><span class="n">jpeg_tag</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" />
|
||
</section>
|
||
<section id="run-time-specified-images-and-image-views">
|
||
<h2><a class="toc-backref" href="#id11" role="doc-backlink">Run-Time Specified Images and Image Views</a><a class="headerlink" href="#run-time-specified-images-and-image-views" title="Link to this heading">¶</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 notranslate"><span class="pre">dynamic_image</span></code> extension allows for working with GIL constructs
|
||
that are specified at run time, also called <em>variants</em>. GIL provides
|
||
models of a run-time instantiated image, <code class="docutils literal notranslate"><span class="pre">any_image</span></code>, and a run-time
|
||
instantiated image view, <code class="docutils literal notranslate"><span class="pre">any_image_view</span></code>. The mechanisms are in
|
||
place to create other variants, such as <code class="docutils literal notranslate"><span class="pre">any_pixel</span></code>,
|
||
<code class="docutils literal notranslate"><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 notranslate"><span class="pre">copy_pixels</span></code> can have
|
||
either or both arguments be variants.</p>
|
||
<p>Let’s make our <code class="docutils literal notranslate"><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 notranslate"><div class="highlight"><pre><span></span><span class="cp">#include</span><span class="w"> </span><span class="cpf"><boost/gil/extension/dynamic_image/dynamic_image_all.hpp></span>
|
||
|
||
<span class="k">template</span><span class="w"> </span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="nc">DstView</span><span class="o">></span>
|
||
<span class="k">struct</span><span class="w"> </span><span class="nc">x_gradient_obj</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">result_type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kt">void</span><span class="p">;</span><span class="w"> </span><span class="c1">// required typedef</span>
|
||
|
||
<span class="w"> </span><span class="n">DstView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">_dst</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="n">x_gradient_obj</span><span class="p">(</span><span class="n">DstView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="n">_dst</span><span class="p">(</span><span class="n">dst</span><span class="p">)</span><span class="w"> </span><span class="p">{}</span>
|
||
|
||
<span class="w"> </span><span class="k">template</span><span class="w"> </span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="nc">SrcView</span><span class="o">></span>
|
||
<span class="w"> </span><span class="k">auto</span><span class="w"> </span><span class="k">operator</span><span class="p">()(</span><span class="n">SrcView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">)</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="o">-></span><span class="w"> </span><span class="n">result_type</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">x_luminosity_gradient</span><span class="p">(</span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">_dst</span><span class="p">);</span>
|
||
<span class="w"> </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 notranslate"><span class="pre">x_luminosity_gradient</span></code> that
|
||
takes an image view variant and calls <code class="docutils literal notranslate"><span class="pre">variant2::visit</span></code> passing it the
|
||
function object:</p>
|
||
<div class="highlight-cpp notranslate"><div class="highlight"><pre><span></span><span class="k">template</span><span class="w"> </span><span class="o"><</span><span class="k">typename</span><span class="w"> </span><span class="p">...</span><span class="n">SrcViews</span><span class="p">,</span><span class="w"> </span><span class="k">typename</span><span class="w"> </span><span class="nc">DstView</span><span class="o">></span>
|
||
<span class="kt">void</span><span class="w"> </span><span class="n">x_luminosity_gradient</span><span class="p">(</span><span class="n">any_image_view</span><span class="o"><</span><span class="n">SrcViews</span><span class="p">...</span><span class="o">></span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">DstView</span><span class="w"> </span><span class="k">const</span><span class="o">&</span><span class="w"> </span><span class="n">dst</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">variant2</span><span class="o">::</span><span class="n">visit</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="w"> </span><span class="n">src</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">any_image_view<SrcViews...></span></code> is the image view variant. It is
|
||
templated over <code class="docutils literal notranslate"><span class="pre">SrcViews...</span></code>, an enumeration of all possible view types
|
||
the variant can take. <code class="docutils literal notranslate"><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 notranslate"><span class="pre">variant2::visit</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 notranslate"><div class="highlight"><pre><span></span><span class="cp">#include</span><span class="w"> </span><span class="cpf"><boost/gil/extension/io/jpeg.hpp></span>
|
||
|
||
<span class="k">using</span><span class="w"> </span><span class="n">my_img</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">any_image</span><span class="o"><</span><span class="n">gray8_image_t</span><span class="p">,</span><span class="w"> </span><span class="n">gray16_image_t</span><span class="p">,</span><span class="w"> </span><span class="n">rgb8_image_t</span><span class="p">,</span><span class="w"> </span><span class="n">rgb16_image_t</span><span class="o">></span><span class="p">;</span>
|
||
<span class="n">my_img</span><span class="w"> </span><span class="n">runtime_image</span><span class="p">;</span>
|
||
<span class="n">read_image</span><span class="p">(</span><span class="s">"input.jpg"</span><span class="p">,</span><span class="w"> </span><span class="n">runtime_image</span><span class="p">,</span><span class="w"> </span><span class="n">jpeg_tag</span><span class="p">{});</span>
|
||
|
||
<span class="n">gray8s_image_t</span><span class="w"> </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="w"> </span><span class="n">view</span><span class="p">(</span><span class="n">gradient</span><span class="p">));</span>
|
||
<span class="n">write_view</span><span class="p">(</span>
|
||
<span class="w"> </span><span class="s">"x_gradient.jpg"</span><span class="p">,</span>
|
||
<span class="w"> </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>
|
||
<span class="w"> </span><span class="n">jpeg_tag</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 an 8 bit signed
|
||
(i.e. <code class="docutils literal notranslate"><span class="pre">char</span></code>) image to store the gradient and invoke <code class="docutils literal notranslate"><span class="pre">x_luminosity_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 notranslate"><span class="pre">read_image</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">dimensions</span></code>, <code class="docutils literal notranslate"><span class="pre">view</span></code> and <code class="docutils literal notranslate"><span class="pre">const_view</span></code> work on both templated and
|
||
variant types. For templated images <code class="docutils literal notranslate"><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 notranslate"><span class="pre">view(runtime_image)</span></code> is
|
||
<code class="docutils literal notranslate"><span class="pre">any_image_view<Views></span></code> where <code class="docutils literal notranslate"><span class="pre">Views</span></code> enumerates four views
|
||
corresponding to the four image types. <code class="docutils literal notranslate"><span class="pre">const_view(runtime_image)</span></code>
|
||
returns an <code class="docutils literal notranslate"><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>
|
||
</section>
|
||
<section id="conclusion">
|
||
<h2><a class="toc-backref" href="#id12" role="doc-backlink">Conclusion</a><a class="headerlink" href="#conclusion" title="Link to this heading">¶</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 <code class="docutils literal notranslate"><span class="pre">x_gradient</span></code> 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,
|
||
<code class="docutils literal notranslate"><span class="pre">channel_convert</span></code> 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>
|
||
</section>
|
||
</section>
|
||
|
||
|
||
<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 2025-12-11 07:12:45.
|
||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 7.2.6.
|
||
</div>
|
||
</body>
|
||
</html> |