-
Notifications
You must be signed in to change notification settings - Fork 3
/
image-affine-mapping-numpy.html
176 lines (150 loc) · 10.8 KB
/
image-affine-mapping-numpy.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
<!DOCTYPE html>
<html lang="en" itemscope itemtype="http://schema.org/Article">
<head>
<title>Image affine mapping in Numpy</title>
<meta charset="utf-8">
<meta property="og:title" content="Image affine mapping in Numpy">
<meta property="og:site_name" content="Modesto Mas | Blog">
<meta property="og:image" content="https://mmas.github.io/images/profile.jpg">
<meta property="og:image:width" content="200">
<meta property="og:image:height" content="200">
<meta property="og:url" content="https://mmas.github.io/image-affine-mapping-numpy">
<meta property="og:locale" content="en_GB">
<meta name="twitter:image" content="https://mmas.github.io/images/profile.jpg">
<meta name="twitter:url" content="https://mmas.github.io/image-affine-mapping-numpy">
<meta name="twitter:card" content="summary">
<meta name="twitter:domain" content="mmas.github.io">
<meta name="twitter:title" content="Image affine mapping in Numpy">
<meta name="description" content="Previously, we implemented linear transformations to a matrix in Numpy. In this case we will apply an affine transformation to an image, mapping three...">
<meta name="twitter:description" content="Previously, we implemented linear transformations to a matrix in Numpy. In this case we will apply an affine transformation to an image, mapping three...">
<meta property="og:description" content="Previously, we implemented linear transformations to a matrix in Numpy. In this case we will apply an affine transformation to an image, mapping three...">
<meta name="keywords" content="geometric-transformations,geometry,image-processing,numpy,python">
<meta property="og:type" content="blog">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta property="og:type" content="article">
<meta property="article:author" content="https://github.com/mmas">
<meta property="article:section" content="geometric-transformations">
<meta property="article:tag" content="geometric-transformations,geometry,image-processing,numpy,python">
<meta property="article:published_time" content="2016-08-18">
<meta property="article:modified_time" content="2016-08-18">
<link rel="stylesheet" type="text/css" href="/css/main.css">
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
CommonHTML: {
scale: 93,
showMathMenu: false
},
tex2jax: {
"inlineMath": [["$","$"], ["\\(","\\)"]]
}
});
</script>
<script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML"></script>
</head>
<body class="entry-detail">
<header>
<div>
<img src="https://mmas.github.io/images/profile.jpg">
<a class="brand" href="/">Modesto Mas</a>
<span>Data/Python/DevOps Engineer</span>
<nav>
<ul>
<li><a href="/tags">Tags</a></li>
<li><a href="https://github.com/mmas/mmas.github.io/issues" target="_blank">Issues</a></li>
</ul>
</nav>
</div>
</header>
<section id="content" role="main">
<article>
<header>
<h1><a href="/image-affine-mapping-numpy">Image affine mapping in Numpy</a></h1>
<time datetime="2016-08-18">Aug 18, 2016</time>
<a class="tag" href="/tags?tag=geometric-transformations">geometric-transformations</a>
<a class="tag" href="/tags?tag=geometry">geometry</a>
<a class="tag" href="/tags?tag=image-processing">image-processing</a>
<a class="tag" href="/tags?tag=numpy">numpy</a>
<a class="tag" href="/tags?tag=python">python</a>
</header>
<aside id="article-nav"></aside>
<section class="body">
<p><a href="/linear-transformations-numpy">Previously</a>, we implemented linear transformations to a matrix in Numpy. In this case we will apply an <a target="_blank" href="https://en.wikipedia.org/wiki/Affine_transformation">affine transformation</a> to an image, mapping three points to the new origin, top right and bottom left corner.</p>
<p>The image (<a target="_blank" href="http://www.freeimages.com/photo/neon-bar-1252445">source</a>) we are going to use in this example is this one:</p>
<p><img style="width:500px" src="https://mmas.github.io/images/figure_1_affine_mapping_numpy.jpg" /><br /></p>
<p>Loading the image flattened, for simplicity, measure the three desired points, in this case <span class="math">\((67, 161)\)</span> (origin), <span class="math">\((482, 51)\)</span> (top right) and <span class="math">\((76, 361)\)</span> (bottom left):</p>
<pre class="sourceCode python"><code class="sourceCode python"><span class="ch">import</span> numpy <span class="ch">as</span> np
<span class="ch">from</span> scipy <span class="ch">import</span> ndimage
<span class="ch">import</span> matplotlib <span class="ch">as</span> mpl
<span class="ch">import</span> matplotlib.pyplot <span class="ch">as</span> plt
mpl.rcParams.update({<span class="st">'image.cmap'</span>: <span class="st">'gray'</span>,
<span class="st">'lines.markersize'</span>: <span class="dv">12</span>,
<span class="st">'axes.prop_cycle'</span>: mpl.cycler(<span class="st">'color'</span>, [<span class="st">'#00ff00'</span>])})
src = ndimage.imread(<span class="st">'neon-bar-1252445.jpg'</span>, flatten=<span class="ot">True</span>)
p = [(<span class="dv">67</span>, <span class="dv">161</span>), (<span class="dv">482</span>, <span class="dv">51</span>), (<span class="dv">76</span>, <span class="dv">361</span>)]
plt.imshow(src)
plt.plot(p[<span class="dv">0</span>][<span class="dv">0</span>], p[<span class="dv">0</span>][<span class="dv">1</span>], <span class="st">'.'</span>)
plt.plot(p[<span class="dv">1</span>][<span class="dv">0</span>], p[<span class="dv">1</span>][<span class="dv">1</span>], <span class="st">'.'</span>)
plt.plot(p[<span class="dv">2</span>][<span class="dv">0</span>], p[<span class="dv">2</span>][<span class="dv">1</span>], <span class="st">'.'</span>)
plt.axis(<span class="st">'image'</span>)
plt.show()</code></pre>
<p><img style="width:500px" src="https://mmas.github.io/images/figure_2_affine_mapping_numpy.png" /><br /></p>
<p>Calculate the distance from the origin to the two corners using Pythagoras theorem to get the height and width of the final image as</p>
<p><span class="math">\[
\begin{align*}
& w = \sqrt{(p_{1x}-p_{0x})^2 + (p_{1y}-p_{0y})^2} \\
& h = \sqrt{(p_{2x}-p_{0x})^2 + (p_{2y}-p_{0y})^2},
\end{align*}
\]</span></p>
<pre class="sourceCode python"><code class="sourceCode python">w = np.sqrt((p[<span class="dv">1</span>][<span class="dv">0</span>]-p[<span class="dv">0</span>][<span class="dv">0</span>])**<span class="dv">2</span>+(p[<span class="dv">1</span>][<span class="dv">1</span>]-p[<span class="dv">0</span>][<span class="dv">1</span>])**<span class="dv">2</span>)
h = np.sqrt((p[<span class="dv">2</span>][<span class="dv">0</span>]-p[<span class="dv">0</span>][<span class="dv">0</span>])**<span class="dv">2</span>+(p[<span class="dv">2</span>][<span class="dv">1</span>]-p[<span class="dv">0</span>][<span class="dv">1</span>])**<span class="dv">2</span>)</code></pre>
<p>The affine transformation has the form</p>
<p><span class="math">\[
f(\mathbf{x}) = \mathbf{A} \mathbf{x} + \mathbf{a},
\]</span></p>
<p>and maps the points <span class="math">\((0,0)\)</span>, <span class="math">\((w,0)\)</span> and <span class="math">\((0,h)\)</span> to the points <span class="math">\(p_0\)</span>, <span class="math">\(p_1\)</span> and <span class="math">\(p_2\)</span>. Then</p>
<p><span class="math">\[
\mathbf{A} =
\begin{pmatrix}
{p_{1x}-p_{0x} \over w} & {p_{2x}-p_{0x} \over h} \\
{p_{1y}-p_{0y} \over w} & {p_{2y}-p_{0y} \over h}
\end{pmatrix}
\quad
\text{and}
\quad
\mathbf{a} =
\begin{pmatrix}
p_{0x} \\
p_{0y}
\end{pmatrix}.
\]</span></p>
<p>Naming <span class="math">\(\mathbf{A}\)</span> as <code>a</code> and <span class="math">\(\mathbf{a}\)</span> as <code>offset</code> to follow the variable naming standards, the linear transformation matrix and offset are:</p>
<pre class="sourceCode python"><code class="sourceCode python">a = np.array([[(p[<span class="dv">1</span>][<span class="dv">0</span>]-p[<span class="dv">0</span>][<span class="dv">0</span>])/w, (p[<span class="dv">2</span>][<span class="dv">0</span>]-p[<span class="dv">0</span>][<span class="dv">0</span>])/h],
[(p[<span class="dv">1</span>][<span class="dv">1</span>]-p[<span class="dv">0</span>][<span class="dv">1</span>])/w, (p[<span class="dv">2</span>][<span class="dv">1</span>]-p[<span class="dv">0</span>][<span class="dv">1</span>])/h]])
offset = p[<span class="dv">0</span>]</code></pre>
<p>We need to reverse a transformation <span class="math">\(f(\mathbf{x})\)</span>, then we can apply the affine transformation in the same way as in the <a href="/linear-transformations-numpy">linear transformation entry</a>, but without inversing the transformation matrix to calculate the new points:</p>
<pre class="sourceCode python"><code class="sourceCode python">M, N = src.shape
points = np.mgrid[<span class="dv">0</span>:N, <span class="dv">0</span>:M].reshape((<span class="dv">2</span>, M*N))
new_points = a.dot(points).<span class="dt">round</span>().astype(<span class="dt">int</span>)</code></pre>
<p>and, since we have to "undo" the displacement by the offset, we need to add this offset to the new points, not subtract it:</p>
<pre class="sourceCode python"><code class="sourceCode python">new_points[<span class="dv">0</span>] += offset[<span class="dv">0</span>]
new_points[<span class="dv">1</span>] += offset[<span class="dv">1</span>]</code></pre>
<p>Then, map the new points like in the previous entry:</p>
<pre class="sourceCode python"><code class="sourceCode python">x, y = new_points.reshape((<span class="dv">2</span>, M, N), order=<span class="st">'F'</span>)
indices = x + N*y
dst = np.take(src, indices, mode=<span class="st">'wrap'</span>)</code></pre>
<p>At this point the three points must be at the origin, the top and the left of the image:</p>
<pre class="sourceCode python"><code class="sourceCode python">plt.imshow(dst)
plt.show()</code></pre>
<p><img style="width:500px" src="https://mmas.github.io/images/figure_3_affine_mapping_numpy.png" /><br /></p>
<p>and we just need to slice the image to the new width and height (<code>w</code> and <code>h</code>):</p>
<pre class="sourceCode python"><code class="sourceCode python">dst = dst[:<span class="dt">int</span>(h)+<span class="dv">1</span>, :<span class="dt">int</span>(w)+<span class="dv">1</span>]
plt.imshow(dst)
plt.show()</code></pre>
<p><img style="width:500px" src="https://mmas.github.io/images/figure_4_affine_mapping_numpy.png" /><br /></p>
</section>
</article>
</section>
<footer></footer>
<script type="text/javascript" src="/js/article.js"></script>
</body>
</html>