Line data Source code
1 : /** \file imagePads.hpp
2 : * \brief Image padding
3 : * \ingroup image_processing_files
4 : * \author Jared R. Males (jaredmales@gmail.com)
5 : *
6 : */
7 :
8 : //***********************************************************************//
9 : // Copyright 2015, 2016, 2017, 2018 Jared R. Males (jaredmales@gmail.com)
10 : //
11 : // This file is part of mxlib.
12 : //
13 : // mxlib is free software: you can redistribute it and/or modify
14 : // it under the terms of the GNU General Public License as published by
15 : // the Free Software Foundation, either version 3 of the License, or
16 : // (at your option) any later version.
17 : //
18 : // mxlib is distributed in the hope that it will be useful,
19 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 : // GNU General Public License for more details.
22 : //
23 : // You should have received a copy of the GNU General Public License
24 : // along with mxlib. If not, see <http://www.gnu.org/licenses/>.
25 : //***********************************************************************//
26 :
27 : #ifndef improc_imagePads_hpp
28 : #define improc_imagePads_hpp
29 :
30 : #include <vector>
31 :
32 : #include "../mxlib.hpp"
33 :
34 : namespace mx
35 : {
36 : namespace improc
37 : {
38 :
39 : /** \addtogroup image_padding
40 : * These functions pad an image by adding rows and columns, and filling in the new pixels with appropriate values. This
41 : * can be a constant, or nearest neighbor. A version is also provided which expands an aribtrary 1/0 mask, which is
42 : * useful when working with, say, circular regions of an image.
43 : */
44 :
45 : /** \ingroup image_padding
46 : * @{
47 : */
48 :
49 : /// Pad an image with a constant value
50 : /**
51 : * \retval 0 on success
52 : * \retval -1 on error
53 : *
54 : * \tparam imOutT is an Eigen-like array
55 : * \tparam imInT is an Eigen-like array
56 : */
57 : template <typename imOutT, typename imInT>
58 0 : int padImage( imOutT &imOut, ///< [out] On return contains the padded image. This will be resized.
59 : imInT &imIn, ///< [in] The image to be padded.
60 : unsigned int padSz, ///< [in] The size of the pad. The padded image (imOut) will be 2*padSz rows and cols
61 : ///< larger than the input.
62 : typename imOutT::Scalar value ///< [in] the value to use for padding.
63 : )
64 : {
65 0 : int nRows = imIn.rows() + 2 * padSz;
66 0 : int nCols = imIn.cols() + 2 * padSz;
67 :
68 0 : imOut = imOutT::Constant( nRows, nCols, value );
69 :
70 0 : imOut.block( padSz, padSz, imIn.rows(), imIn.cols() ) = imIn;
71 :
72 0 : return 0;
73 : }
74 :
75 : /// Pad an image with a constant value for reference types.
76 : /** This version can be used with Eigen reference types.
77 : *
78 : * \retval 0 on success
79 : * \retval -1 on error
80 : *
81 : * \tparam imOutT is an Eigen-like array reference
82 : * \tparam imInT is an Eigen-like array reference
83 : */
84 : template <typename imOutT, typename imInT>
85 : int padImageRef( imOutT imOut, ///< [out] On return contains the padded image. This will be resized.
86 : imInT imIn, ///< [in] The image to be padded.
87 : unsigned int padSz, ///< [in] The size of the pad. The padded image (imOut) will be 2*padSz rows and
88 : ///< cols larger than the input.
89 : typename imOutT::Scalar value ///< [in] the value to use for padding.
90 : )
91 : {
92 : int nRows = imIn.rows() + 2 * padSz;
93 : int nCols = imIn.cols() + 2 * padSz;
94 :
95 : imOut = imOutT::Constant( nRows, nCols, value );
96 :
97 : imOut.block( padSz, padSz, imIn.rows(), imIn.cols() ) = imIn;
98 :
99 : return 0;
100 : }
101 :
102 : /// Pad an image by repeating the values in the edge rows and columns
103 : /** Allocates imOut to hold 2*padSz more rows and columns, and copies imIn to the center. Then fills in the new pixels
104 : * by copying the edge pixels outward.
105 : *
106 : * \retval 0 on success
107 : * \retval -1 on error
108 : *
109 : * \tparam imOutT is an Eigen-like array
110 : * \tparam imInT is an Eigen-like array
111 : */
112 : template <typename imOutT, typename imInT>
113 : int padImage( imOutT &imOut, ///< [out] On return contains the padded image. This will be resized.
114 : imInT &imIn, ///< [in] The image to be padded.
115 : unsigned int padSz ///< [in] The size of the pad. The padded image (imOut) will be 2*padSz rows and cols
116 : ///< larger than the input.
117 : )
118 : {
119 : int dim1 = imIn.rows();
120 : int dim2 = imIn.cols();
121 :
122 : imOut.resize( dim1 + 2 * padSz, dim2 + 2 * padSz );
123 :
124 : imOut.block( padSz, padSz, dim1, dim2 ) = imIn;
125 :
126 : // First do the left and right edge columns and corners
127 : for( int i = 0; i < padSz; ++i )
128 : {
129 : for( int j = 0; j < imOut.cols(); ++j )
130 : {
131 : if( j < padSz )
132 : {
133 : imOut( i, j ) = imIn( 0, 0 );
134 : imOut( imOut.rows() - 1 - i, j ) = imIn( dim1 - 1, 0 );
135 : }
136 : else if( j >= dim2 + padSz )
137 : {
138 : imOut( i, j ) = imIn( 0, dim2 - 1 );
139 : imOut( imOut.rows() - 1 - i, j ) = imIn( dim1 - 1, dim2 - 1 );
140 : }
141 : else
142 : {
143 : imOut( i, j ) = imOut( padSz, j );
144 : imOut( imOut.rows() - padSz + i, j ) = imOut( padSz + dim1 - 1, j );
145 : }
146 : }
147 : }
148 :
149 : // Now do the top and bottom rows.
150 : for( int i = padSz; i < dim1 + padSz; ++i )
151 : {
152 : for( int j = 0; j < padSz; ++j )
153 : {
154 : imOut( i, j ) = imOut( i, padSz );
155 : imOut( i, imOut.cols() - 1 - j ) = imOut( i, imOut.cols() - 1 - padSz );
156 : }
157 : }
158 :
159 : return 0;
160 : }
161 :
162 : /// Pad an image by repeating the values at the edge of a 1/0 mask
163 : /** Allocates imOut to match imMask, and copies imIn to the center multiplied by the mask. Then fills in the new pixels
164 : * by copying the edge pixels outward. For each pixel on the edge of the mask (touching at least one 1-valued pixel
165 : * in the mask), the value is chosen as the value of the nearest pixel with a 1 in the mask. If the closest pixels are
166 : * more than one equidistant pixels, their mean value is used.
167 : *
168 : *
169 : * \retval 0 on success
170 : * \retval -1 on error
171 : *
172 : * \tparam imOutT is an Eigen-like array
173 : * \tparam imInT is an Eigen-like array
174 : */
175 : template <typename imOutT, typename imInT>
176 : int padImage( imOutT &imOut, ///< [out] On return contains the padded image. This will be resized.
177 : imInT &imIn, ///< [in] The image to be padded.
178 : imInT &imMask, ///< [in] The 1/0 mask image.
179 : unsigned int padSz ///< [in] The number of iterations of the padding loop.
180 : )
181 : {
182 :
183 : typedef imInT imMaskT;
184 :
185 : int dim1 = imMask.rows();
186 : int dim2 = imMask.cols();
187 :
188 : imOut.resize( dim1, dim2 );
189 :
190 : int stR = 0.5 * ( dim1 - imIn.rows() );
191 : int stC = 0.5 * ( dim2 - imIn.cols() );
192 :
193 : if( stR < 0 )
194 : {
195 : internal::mxlib_error_report(error_t::invalidarg, "imMask must have at least as many rows as imIn." );
196 : return -1;
197 : }
198 : if( stC < 0 )
199 : {
200 : internal::mxlib_error_report(error_t::invalidarg, "imMask must have at least as many columns as imIn." );
201 : return -1;
202 : }
203 :
204 : imOut.block( stR, stC, imIn.rows(), imIn.cols() ) = imIn;
205 :
206 : imOut *= imMask;
207 :
208 : imMaskT tMask = imMask; // Need a modifiable mask, which grows as the image is extended
209 :
210 : std::vector<int> nmi, nmj; // Used to track which pixels of the mask need to be updated
211 :
212 : for( unsigned int n = 0; n < padSz; ++n )
213 : {
214 : nmi.clear();
215 : nmj.clear();
216 : for( int ii = 0; ii < dim1; ++ii )
217 : {
218 : for( int jj = 0; jj < dim2; ++jj )
219 : {
220 : if( tMask( ii, jj ) == 1 )
221 : continue;
222 :
223 : typename imOutT::Scalar mv = 0;
224 : // int r, minr;// = 3;
225 : int nv = 0;
226 :
227 : // Loop over the neighboring pixels
228 : for( int i = -1; i <= 1; ++i )
229 : {
230 : for( int j = -1; j <= 1; ++j )
231 : {
232 : if( i == 0 && j == 0 )
233 : continue;
234 : if( ii + i < 0 || ii + i >= dim1 )
235 : continue;
236 : if( jj + j < 0 || jj + j >= dim2 )
237 : continue;
238 :
239 : if( tMask( ii + i, jj + j ) == 1 )
240 : {
241 : mv += imOut( ii + i, jj + j );
242 : ++nv;
243 : }
244 : }
245 : }
246 :
247 : // Found at least 1 neighboring pixel in the 1's part of the mask
248 : if( nv > 0 )
249 : {
250 : imOut( ii, jj ) = mv / nv;
251 : nmi.push_back( ii );
252 : nmj.push_back( jj );
253 : }
254 : }
255 : }
256 :
257 : for( size_t i = 0; i < nmi.size(); ++i )
258 : {
259 : tMask( nmi[i], nmj[i] ) = 1;
260 : }
261 : }
262 :
263 : return 0;
264 : }
265 :
266 : /// Cut down a padded image
267 : /**
268 : * \retval 0 on success
269 : * \retval -1 on error
270 : *
271 : * \tparam imOutT is an Eigen-like array
272 : * \tparam imInT is an Eigen-like array
273 : */
274 : template <typename imOutT, typename imInT>
275 : int cutPaddedImage(
276 : imOutT &imOut, ///< [out] On return contains the cut image. This will be resized.
277 : const imInT &imIn, ///< [in] The image to be cut down.
278 : unsigned int padSz ///< [in] The size of the pad. The output image will be smaller by 2*padSz rows and cols.
279 : )
280 : {
281 : if( 2 * padSz > imIn.rows() )
282 : {
283 : internal::mxlib_error_report(error_t::invalidarg, "padSz too large, must be < imIn.rows()/2" );
284 : return -1;
285 : }
286 :
287 : if( 2 * padSz > imIn.cols() )
288 : {
289 : internal::mxlib_error_report(error_t::invalidarg, "padSz too large, must be < imIn.cols()/2" );
290 : return -1;
291 : }
292 :
293 : int nRows = imIn.rows() - 2 * padSz;
294 : int nCols = imIn.cols() - 2 * padSz;
295 :
296 : imOut = imIn.block( padSz, padSz, nRows, nCols );
297 :
298 : return 0;
299 : }
300 :
301 : ///@}
302 :
303 : } // namespace improc
304 : } // namespace mx
305 :
306 : #endif // improc_imagePads_hpp
|