mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
imageMasks.hpp
Go to the documentation of this file.
1/** \file imageMasks.hpp
2 * \brief Declares and defines functions to work with image masks
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_imageMasks_hpp
28#define improc_imageMasks_hpp
29
30#include "../math/constants.hpp"
31#include "../math/geo.hpp"
32
33#include "eigenImage.hpp"
34#include "imageTransforms.hpp"
35
36namespace mx
37{
38
39namespace improc
40{
41
42/// Fills in the cells of an Eigen 2D Array with their radius from the center
43/** \ingroup image_masks
44 *
45 * \tparam eigenT is an Eigen-like 2D array type
46 */
47template <class eigenT>
48void radiusImage( eigenT &m, ///< [out] the allocated radius array, will be filled in with radius values.
49 typename eigenT::Scalar xc, ///< [in] the x center
50 typename eigenT::Scalar yc, ///< [in] the y center
51 typename eigenT::Scalar scale = 1 ///< [in] [optional] a scaling to apply to each value (default = 1)
52)
53{
54 typedef typename eigenT::Scalar arithT;
55
56 arithT f_x, f_y;
57
58 size_t dim1 = m.rows();
59 size_t dim2 = m.cols();
60
61 for( size_t i = 0; i < dim1; i++ )
62 {
63 f_x = ( i - xc ) * ( i - xc );
64
65 for( size_t j = 0; j < dim2; j++ )
66 {
67 f_y = ( j - yc ) * ( j - yc );
68
69 m( i, j ) = sqrt( f_x + f_y ) * scale;
70 }
71 }
72}
73
74/// Fills in the cells of Eigen-like 2D Array with their radius from the canonical center
75/** \ingroup image_masks
76 *
77 * The center is @f$ (x_c, y_c) = (0.5*(dim_1-1), 0.5*(dim_2 -1)) @f$.
78 *
79 * \tparam eigenT is an Eigen-like 2D array type
80 */
81template <class eigenT>
82void radiusImage( eigenT &m, /**< [out] the allocated radius array,
83 will be filled in with radius values.*/
84 typename eigenT::Scalar scale = 1 /**< [in] [optional] a scaling to apply to
85 each value (default = 1) */ )
86{
87 typedef typename eigenT::Scalar arithT;
88
89 arithT xc, yc;
90
91 xc = 0.5 * ( m.rows() - 1 );
92 yc = 0.5 * ( m.cols() - 1 );
93
94 radiusImage( m, xc, yc, scale );
95}
96
97/// Fills in the cells of an Eigen-like 2D Array with their angle relative to the center
98/** \ingroup image_masks
99 *
100 * \tparam angleT is the angle type, either radiansT<realT> or degreesT<realT>. Note that realT sets the type for all
101 * arithmetic. \tparam eigenT is an Eigen-like 2D array type
102 */
103template <class angleT, class eigenT>
104void angleImage( eigenT &m, /**< [out] the allocated angle array. Will be filled
105 in with angle values.*/
106 typename angleT::realT xc, /**< [in] the x center*/
107 typename angleT::realT yc /**< [in] the y center */ )
108{
109 typedef typename angleT::realT realT;
110
111 for( size_t j = 0; j < m.cols(); j++ )
112 {
113 realT f_y = ( static_cast<realT>( j ) - yc );
114
115 for( size_t i = 0; i < m.rows(); i++ )
116 {
117 realT f_x = ( static_cast<realT>( i ) - xc );
118
119 m( i, j ) = fmod( atan2( f_y, f_x ) + math::two_pi<realT>(), math::two_pi<realT>() ) * angleT::scale;
120 }
121 }
122}
123
124/// Fills in the cells of Eigen 2D Array with their angle relative the canonical center
125/** \ingroup image_masks
126 *
127 * The center is @f$ (x_c, y_c) = (0.5*(dim_1-1), 0.5*(dim_2 -1)) @f$.
128 *
129 * \tparam angleT is the angle type, either radiansT<realT> or degreesT<realT>. Note that realT sets the type for all
130 * arithmetic. \tparam eigenT is an Eigen-like 2D array type
131 */
132template <class angleT, class eigenT>
133void angleImage( eigenT &m /** < [out] the allocated angle array. Will be filled in with angle values. */ )
134{
135 typedef typename angleT::realT realT;
136
137 realT xc = 0.5 * ( m.rows() - 1 );
138 realT yc = 0.5 * ( m.cols() - 1 );
139
140 angleImage<angleT>( m, xc, yc );
141}
142
143/// Fills in the cells of Eigen-like arrays with their radius amd angle relative to the center
144/** \ingroup image_masks
145 *
146 * \tparam angleT is the angle type, either radiansT<realT> or degreesT<realT>. Note that realT sets the type for all
147 * arithmetic. \tparam eigenT1 is an Eigen-like 2D array type. Should be resolved by compiler. \tparam eigenT2 is an
148 * Eigen-like 2D array type. Should be resolved by compiler.
149 *
150 */
151template <class angleT, class eigenT1, class eigenT2>
152void radAngImage( eigenT1 &rIm, /**< [out] the allocated radius array, will be
153 filled in with radius values.*/
154 eigenT2 &qIm, /**< [out] the angle array, will be re-sized to match rIm.
155 Will be filled in with angle values.*/
156 typename angleT::realT xc, /**< [in] the x center*/
157 typename angleT::realT yc, /**< [in] the y center*/
158 typename angleT::realT rscale = 1 /**< [in] [optional] a scaling to apply to each
159 radius value. Default is 1.0.*/)
160{
161 typedef typename angleT::realT realT;
162
163 qIm.resize( rIm.rows(), rIm.cols() );
164
165 for( int cc = 0; cc < rIm.cols(); ++cc )
166 {
167 realT f_y = ( ( static_cast<realT>( cc ) ) - yc );
168
169 for( int rr = 0; rr < rIm.rows(); ++rr )
170 {
171 realT f_x = ( ( static_cast<realT>( rr ) ) - xc );
172
173 rIm( rr, cc ) = std::sqrt( f_x * f_x + f_y * f_y ) * rscale;
174 qIm( rr, cc ) = fmod( atan2( f_y, f_x ) + math::two_pi<realT>(), math::two_pi<realT>() ) * angleT::scale;
175 }
176 }
177}
178
179template <typename vecT>
180struct maskCoordFormat;
181
182template <>
183struct maskCoordFormat<std::vector<size_t>>
184{
185 static size_t coord( int i, int j, int rows )
186 {
187 return j * rows + i;
188 }
189};
190
191template <>
192struct maskCoordFormat<std::vector<std::vector<int>>>
193{
194 static std::vector<int> coord( int i, int j, int rows )
195 {
196 return std::vector<int>( { i, j } );
197 }
198};
199
200/// Get the coordinates of an annular region in an image
201/**
202 * \ingroup image_masks
203 *
204 * \tparam angleT is the angle type, either radiansT<realT> or degreesT<realT>. Note that realT sets the type for all
205 * arithmetic.
206 * \tparam eigenT1 is an Eigen-like 2D array type. Should be resolved by compiler.
207 * \tparam eigenT2 is an Eigen-like 2D array type. Should be resolved by compiler.
208 * \tparam eigenT3 is an Eigen-like 2D array type. Should be resolved by compiler.
209 *
210 * \returns a vector containing the coordinates formatted bt maskCoordFormat of the region defined
211 * by the input parameters
212 */
213template <typename vecT, typename angleT, typename eigenT1, typename eigenT2, typename eigenT3 = eigenT1>
214vecT annulusCoordsWorker( const eigenT1 &rIm, /**< [in] a radius image of the type produced by
215 \ref radiusImage */
216 const eigenT2 &qIm, /**< [in] an angle image of the type produce by
217 \ref angleImage */
218 typename angleT::realT xcen, /**< [in] the x center of the image */
219 typename angleT::realT ycen, /**< [in] the y center of the image */
220 typename angleT::realT min_r, /**< [in] the minimum radius of the region */
221 typename angleT::realT max_r, /**< [in] the maximum radius of the region */
222 typename angleT::realT min_q, /**< [in] the minimum angle of the region. */
223 typename angleT::realT max_q, /**< [in] the maximum angle of the region. */
224 eigenT3 *mask = 0, /**< [in] [optional] pointer to a mask image, only
225 pixels of value 1 are included
226 in the indices. */
227 typename angleT::realT pixbuf = 0 /**< [in] [optional] pixel buffer for
228 rad comparisons */ )
229{
230
231 vecT idx;
232
233 int max_x = max_r;
234 int min_x = -max_x;
235 int max_y = max_r;
236 int min_y = -max_y;
237
238 int x0 = xcen + min_x;
239 if( x0 < 0 )
240 x0 = 0;
241
242 int x1 = xcen + max_x + 1;
243 if( x1 > rIm.rows() )
244 x1 = rIm.rows();
245
246 int y0 = ycen + min_y;
247 if( y0 < 0 )
248 y0 = 0;
249
250 int y1 = ycen + max_y + 1;
251 if( y1 > rIm.cols() )
252 y1 = rIm.cols();
253
254 // Normalize the angles by mod
255 min_q = math::angleMod<angleT>( min_q );
256 // If max_q is exactly 360/2pi (to within a tol of 100*eps) we don't mod, other mod.
257 if( fabs( max_q - angleT::full ) > 100 * std::numeric_limits<typename angleT::realT>::epsilon() )
258 {
259 max_q = math::angleMod<angleT>( max_q );
260 }
261
262 // Find the mid-point to enable sectors wider than 180.
263 typename angleT::realT mid_q;
264 if( min_q <= max_q )
265 {
266 mid_q = 0.5 * ( min_q + max_q );
267 }
268 else
269 {
270 mid_q = 0.5 * ( ( min_q - angleT::full ) + max_q );
271 }
272
273 for( int j = y0; j < y1; ++j )
274 {
275 for( int i = x0; i < x1; ++i )
276 {
277 if( rIm( i, j ) < min_r - pixbuf )
278 {
279 continue;
280 }
281 if( rIm( i, j ) >= max_r + pixbuf )
282 {
283 continue;
284 }
285 if( !( ( math::angleDiff<angleT>( min_q, qIm( i, j ) ) >= 0 &&
286 math::angleDiff<angleT>( qIm( i, j ), mid_q ) > 0 ) ||
287 ( math::angleDiff<angleT>( mid_q, qIm( i, j ) ) >= 0 &&
288 math::angleDiff<angleT>( qIm( i, j ), max_q ) > 0 ) ) )
289 {
290 continue;
291 }
292
293 if( mask )
294 {
295 if( ( *mask )( i, j ) == 0 )
296 {
297 continue;
298 }
299 }
300
301 idx.push_back( maskCoordFormat<vecT>::coord( i, j, rIm.rows() ) );
302 }
303 }
304
305 return idx;
306}
307
308/// Get the array coordinates of an annular region in an image
309/**
310 * \ingroup image_masks
311 *
312 * \tparam angleT is the angle type, either radiansT<realT> or degreesT<realT>. Note that realT sets the type for all
313 * arithmetic.
314 * \tparam eigenT1 is an Eigen-like 2D array type. Should be resolved by compiler.
315 * \tparam eigenT2 is an Eigen-like 2D array type. Should be resolved by compiler.
316 * \tparam eigenT3 is an Eigen-like 2D array type. Should be resolved by compiler.
317 *
318 * \returns a vector containing vectors of the 2D indices of the region defined by the input parameters.
319 */
320template <typename angleT, typename eigenT1, typename eigenT2, typename eigenT3 = eigenT1>
321std::vector<std::vector<int>> annulusCoords( const eigenT1 &rIm, /**< [in] a radius image of the type produced by
322 \ref radiusImage */
323 const eigenT2 &qIm, /**< [in] an angle image of the type produce by
324 \ref angleImage */
325 typename angleT::realT xcen, /**< [in] the x center of the image */
326 typename angleT::realT ycen, /**< [in] the y center of the image */
327 typename angleT::realT min_r, /**< [in] the minimum radius of the region */
328 typename angleT::realT max_r, /**< [in] the maximum radius of the region */
329 typename angleT::realT min_q, /**< [in] the minimum angle of the region. */
330 typename angleT::realT max_q, /**< [in] the maximum angle of the region. */
331 eigenT3 *mask = 0, /**< [in] [optional] pointer to a mask image, only
332 pixels of value 1 are included
333 in the indices. */
334 typename angleT::realT pixbuf = 0 /**< [in] [optional] pixel buffer for
335 rad comparisons */
336
337)
338{
339 return annulusCoordsWorker<std::vector<std::vector<int>>, angleT, eigenT1, eigenT2, eigenT3>( rIm,
340 qIm,
341 xcen,
342 ycen,
343 min_r,
344 max_r,
345 min_q,
346 max_q,
347 mask );
348}
349
350/// Get the vector indices of an annular region in an image
351/**
352 * \ingroup image_masks
353 *
354 * \tparam angleT is the angle type, either radiansT<realT> or degreesT<realT>. Note that realT sets the type for all
355 * arithmetic.
356 * \tparam eigenT1 is an Eigen-like 2D array type. Should be resolved by compiler.
357 * \tparam eigenT2 is an Eigen-like 2D array type. Should be resolved by compiler.
358 * \tparam eigenT3 is an Eigen-like 2D array type. Should be resolved by compiler.
359 *
360 * \returns a vector containing the 1D indices of the region defined by the input parameters
361 */
362template <typename angleT, typename eigenT1, typename eigenT2, typename eigenT3 = eigenT1>
363std::vector<size_t> annulusIndices( const eigenT1 &rIm, /**< [in] a radius image of the type produced by
364 \ref radiusImage */
365 const eigenT2 &qIm, /**< [in] an angle image of the type produce by
366 \ref angleImage */
367 typename angleT::realT xcen, /**< [in] the x center of the image */
368 typename angleT::realT ycen, /**< [in] the y center of the image */
369 typename angleT::realT min_r, /**< [in] the minimum radius of the region */
370 typename angleT::realT max_r, /**< [in] the maximum radius of the region */
371 typename angleT::realT min_q, /**< [in] the minimum angle of the region. */
372 typename angleT::realT max_q, /**< [in] the maximum angle of the region. */
373 eigenT3 *mask = 0, /**< [in] [optional] pointer to a mask image, only
374 pixels of value 1 are included
375 in the indices. */
376 typename angleT::realT pixbuf = 0 /**< [in] [optional] pixel buffer for
377 rad comparisons */
378
379)
380{
381 return annulusCoordsWorker<std::vector<size_t>, angleT, eigenT1, eigenT2, eigenT3>( rIm,
382 qIm,
383 xcen,
384 ycen,
385 min_r,
386 max_r,
387 min_q,
388 max_q,
389 mask );
390}
391
392/// Get the coordinates of the bounding rectangle of an annulus.
393/**
394 * \ingroup image_masks
395 *
396 * \tparam angleT is the angle type, either radiansT<realT> or degreesT<realT>. Note that realT sets the type for all
397 * arithmetic.
398 *
399 */
400template <typename angleT>
401void annulusBoundingRect( int &x0, ///< [out] The lower left x-coordinate of the bounding rect.
402 int &y0, ///< [out] The lower left y-coordinate of the bounding rect.
403 int &x1, ///< [out] The upper right x-coordinate of the bounding rect.
404 int &y1, ///< [out] The upper right y-coordinate of the bounding rect.
405 typename angleT::realT xcen, ///< [in] the x center of the image
406 typename angleT::realT ycen, ///< [in] the y center of the image
407 typename angleT::realT min_r, ///< [in] the minimum radius of the region
408 typename angleT::realT max_r, ///< [in] the maximum radius of the region
409 typename angleT::realT min_q, ///< [in] the minimum angle of the region.
410 typename angleT::realT max_q, ///< [in] the maximum angle of the region.
411 typename angleT::realT pixbuf = 0 /**< [in] [optional] the pixel buffer for radius
412 comparisons */
413)
414{
415 typedef typename angleT::realT realT;
416
417 // Get the corners
418 realT x00 = xcen + ( min_r - pixbuf ) * cos( min_q * angleT::radians );
419 realT y00 = ycen + ( min_r - pixbuf ) * sin( min_q * angleT::radians );
420 realT x01 = xcen + ( max_r + pixbuf ) * cos( min_q * angleT::radians );
421 realT y01 = ycen + ( max_r + pixbuf ) * sin( min_q * angleT::radians );
422
423 realT x10 = xcen + ( min_r - pixbuf ) * cos( max_q * angleT::radians );
424 realT y10 = ycen + ( min_r - pixbuf ) * sin( max_q * angleT::radians );
425 realT x11 = xcen + ( max_r + pixbuf ) * cos( max_q * angleT::radians );
426 realT y11 = ycen + ( max_r + pixbuf ) * sin( max_q * angleT::radians );
427
428 // vertex of min_r is probably not necessary, but might as well
429 realT x20 = xcen + ( min_r - pixbuf ) * cos( math::angleMean<angleT>( { min_q, max_q } ) * angleT::radians );
430 realT y20 = ycen + ( min_r - pixbuf ) * sin( math::angleMean<angleT>( { min_q, max_q } ) * angleT::radians );
431
432 // vertex of max_r
433 realT x21 = xcen + ( max_r + pixbuf ) * cos( math::angleMean<angleT>( { min_q, max_q } ) * angleT::radians );
434 realT y21 = ycen + ( max_r + pixbuf ) * sin( math::angleMean<angleT>( { min_q, max_q } ) * angleT::radians );
435
436 x0 = std::ceil( std::min( { x00, x01, x10, x11, x20, x21 } ) );
437 y0 = std::ceil( std::min( { y00, y01, y10, y11, y20, y21 } ) );
438 x1 = std::ceil( std::max( { x00, x01, x10, x11, x20, x21 } ) );
439 y1 = std::ceil( std::max( { y00, y01, y10, y11, y20, y21 } ) );
440}
441
442/// Reflect vector indices across the given center pixel.
443/** This assumes column major order.
444 * \returns the vector of reflected indices
445 */
446template <typename realT>
447std::vector<size_t> reflectImageIndices( const std::vector<size_t> &idxi, ///< [in] the vector indices to reflect
448 int w, ///< [in] the image width
449 int h, ///< [in] the image height
450 realT xc, ///< [in] the image center x coordinate
451 realT yc ///< [in] the image center y coordinate
452)
453{
454 std::vector<size_t> idxr;
455 idxr.reserve( idxi.size() );
456
457 int x0, y0, x1, y1;
458 for( size_t n = 0; n < idxi.size(); ++n )
459 {
460 int y0 = idxi[n] / w;
461 int x0 = idxi[n] - y0 * w;
462
463 reflectImageCoords( x1, y1, x0, y0, xc, yc );
464
465 if( x1 < w && y1 < h )
466 idxr.push_back( y1 * w + x1 );
467 }
468
469 return idxr;
470}
471
472template <typename sizeT>
473void rectangleIndices( std::vector<sizeT> &idx, sizeT rows, sizeT cols, sizeT xmin, sizeT xmax, sizeT ymin, sizeT ymax )
474{
475
476 // if(xmin < 0) xmin = 0;
477 if( xmax > rows - 1 )
478 xmax = rows - 1;
479
480 // if(ymin < 0) ymin = 0;
481 if( ymax > cols - 1 )
482 ymax = cols - 1;
483
484 idx.reserve( ( xmax - xmin + 1 ) * ( ymax - ymin + 1 ) );
485
486 for( sizeT i = xmin; i <= xmax; ++i )
487 {
488 for( sizeT j = ymin; j <= ymax; ++j )
489 {
490 idx.push_back( j * rows + i );
491 }
492 }
493}
494
495template <class eigenT>
496void rectangleIndices( std::vector<size_t> &idx, eigenT &mask, size_t xmin, size_t xmax, size_t ymin, size_t ymax )
497{
498 rectangleIndices<size_t>( idx, (size_t)mask.rows(), (size_t)mask.cols(), xmin, xmax, ymin, ymax );
499}
500
501/// Apply a mask to an image
502/** The pixels indicated by the indices are set to a value.
503 *
504 * \ingroup image_masks
505 *
506 * \tparam eigenT is an Eigen-like 2D array type
507 *
508 */
509template <class eigenT>
510void applyMask( eigenT &maskedIm, ///< [out] the image to mask (will be modified)
511 const std::vector<size_t> &idx, ///< [in] the indices of the pixels to mask
512 typename eigenT::Scalar maskval ///< [in] the mask value.
513)
514{
515 for( size_t i = 0; i < idx.size(); ++i )
516 {
517 maskedIm( idx[i] ) = maskval;
518 }
519}
520
521/// Apply a mask to an image
522/** The pixels indicated by the coordinates in a vector are set to a value.
523 *
524 * \ingroup image_masks
525 *
526 * \tparam eigenT is an Eigen-like 2D array type
527 *
528 */
529template <class eigenT>
530void applyMask( eigenT &maskedIm, ///< [out] the image to mask (will be modified)
531 const std::vector<std::vector<int>> &coord, ///< [in] the coordinates of the pixels to mask
532 typename eigenT::Scalar maskval ///< [in] the mask value.
533)
534{
535 for( size_t i = 0; i < coord.size(); ++i )
536 {
537 maskedIm( coord[i][0], coord[i][1] ) = maskval;
538 }
539}
540
541/// Mask a circle in an image.
542/** The circle is describe by its center coordinates and radius. Any value can be set for the mask.
543 * Pixels outside the masked circle are not altered.
544 *
545 * \tparam arrayT is an Eigen-like type.
546 *
547 * \ingroup image_masks
548 */
549template <class arrayT>
550void maskCircle( arrayT &m, /**< [in.out] the image to be masked, is modified. */
551 typename arrayT::Scalar xcen, /**< [in] the x coordinate of the center of the circle */
552 typename arrayT::Scalar ycen, /**< [in] the y coordinate of the center of the circle */
553 typename arrayT::Scalar rad, /**< [in] the radius of the circle */
554 typename arrayT::Scalar val, /**< [in] the mask value. */
555 typename arrayT::Scalar pixbuf = 0.5 /**< [in] [optional] buffer for radius comparison.
556 Default is 0.5 pixels.*/
557)
558{
559 size_t l0 = m.rows();
560 size_t l1 = m.cols();
561
562 typename arrayT::Scalar rr;
563
564 for( size_t c = 0; c < m.cols(); c++ )
565 {
566 for( size_t r = 0; r < m.rows(); r++ )
567 {
568 rr = sqrt( pow( r - xcen, 2 ) + pow( c - ycen, 2 ) );
569
570 if( rr <= rad + pixbuf )
571 m( r, c ) = val;
572 }
573 }
574}
575
576/// Mask a circle in an image at the standard center.
577/** The circle is centered at 0.5*(rows-1) and 0.5*(cols-1), and described by its radius. Any value can be set for the
578 * mask. Pixels outside the masked circle are not altered.
579 *
580 * \tparam arrayT is an Eigen-like type.
581 *
582 * \ingroup image_masks
583 */
584template <class arrayT>
586 arrayT &m, ///< [in.out] the image to be masked, is modified.
587 typename arrayT::Scalar rad, ///< [in] the radius of the circle
588 typename arrayT::Scalar val, ///< [in] the mask value.
589 typename arrayT::Scalar pixbuf = 0.5 ///< [in] [optional] buffer for radius comparison. Default is 0.5 pixels.
590)
591{
592 return maskCircle( m, 0.5 * ( m.rows() - 1.0 ), 0.5 * ( m.cols() - 1.0 ), rad, val, pixbuf );
593}
594
595/// Mask an ellipse in an image.
596/** The ellipse is describe by its center coordinates and x and y direction radii (the semi-major and -minor axes,
597 * in either order). Any value can be set for the mask,cwith 0 being the default.
598 *
599 * \tparam arrayT is an Eigen-like type.
600 *
601 * \ingroup image_masks
602 */
603template <class arrayT>
604void maskEllipse( arrayT &m, ///< [in.out] the image to be masked, is modified.
605 typename arrayT::Scalar xcen, ///< [in] the x coordinate of the center of the ellipse
606 typename arrayT::Scalar ycen, ///< [in] the y coordinate of the center of the ellipse
607 typename arrayT::Scalar xrad, ///< [in] the x radius of the ellipse
608 typename arrayT::Scalar yrad, ///< [in] the y radius of the ellipse
609 typename arrayT::Scalar ang, ///< [in] the c.c.w. angle to rotate the ellipse by
610 typename arrayT::Scalar val = 0, ///< [in] [optional] the mask value. Default is 0.
611 typename arrayT::Scalar pixbuf = 0.5 /**< [in] [optional] buffer for radius comparison.
612 Default is 0.5 pixels.*/
613)
614{
615 typedef typename arrayT::Scalar realT;
616
617 size_t l0 = m.rows();
618 size_t l1 = m.cols();
619
620 realT r;
621 realT x;
622 realT y;
623 realT xe, ye;
624 realT rad;
625
626 realT cq = cos( ang );
627 realT sq = sin( ang );
628
629 realT xr, yr;
630
631 for( size_t i = 0; i < l0; i++ )
632 {
633 x = i - xcen;
634 for( size_t j = 0; j < l1; j++ )
635 {
636 y = j - ycen;
637
638 if( x == 0 && y == 0 )
639 {
640 m( i, j ) = val;
641 }
642 else
643 {
644 xr = x * cq - y * sq;
645 yr = x * sq + y * cq;
646
647 // Coordinate on the ellipse where the line to the point intersects
648 xe = ( pow( xrad * yrad, 2 ) / ( pow( yrad, 2 ) + pow( xrad * yr / xr, 2 ) ) );
649 ye = ( pow( yrad, 2 ) - xe * pow( yrad / xrad, 2 ) );
650
651 rad = sqrt( xe + ye );
652
653 r = sqrt( pow( xr, 2 ) + pow( yr, 2 ) );
654
655 if( r <= rad + pixbuf )
656 {
657 m( i, j ) = val;
658 }
659 }
660 }
661 }
662}
663
664/// Mask a wedge in an image.
665/** The wedge is describe by its center coordinate, central angle, and angular half-width. Any value can be set for the
666 * mask. Pixels outside the masked circle are not altered.
667 *
668 * \tparam arrayT is an Eigen-like type.
669 *
670 * \ingroup image_masks
671 */
672template <class arrayT>
673void maskWedge( arrayT &m, ///< [in.out] the image to be masked, is modified.
674 typename arrayT::Scalar xcen, ///< [in] the x coordinate of the center of the circle
675 typename arrayT::Scalar ycen, ///< [in] the y coordinate of the center of the circle
676 typename arrayT::Scalar angCen, ///< [in] the central angle of the wedge, in degrees
677 typename arrayT::Scalar angHW, ///< [in] the angular half-wdith of the wedge, in degrees
678 typename arrayT::Scalar val = 0 ///< [in] [optional] the mask value. Default is 0.
679)
680{
681 size_t l0 = m.rows();
682 size_t l1 = m.cols();
683
684 typedef typename arrayT::Scalar angleT;
685
686 angleT dang;
687
688 for( size_t c = 0; c < m.cols(); c++ )
689 {
690 for( size_t r = 0; r < m.rows(); r++ )
691 {
692
693 dang = math::angleDiff<math::degreesT<angleT>>( math::rtod( atan2( c - ycen, r - xcen ) ), angCen );
694
695 if( dang > -angHW && dang <= angHW )
696 {
697 m( r, c ) = val;
698 }
699 }
700 }
701}
702
703/// Draw a thin (1-pixel) line from one point to another.
704/** Calculates the line connecting the two points and sets the pixels along that line to the
705 * supplied value.
706 *
707 * \tparam realT a real floating point type
708 *
709 * \ingroup image_masks
710 */
711template <typename realT>
712int drawLine( eigenImage<realT> &mask, ///< [in/out] [pre-allocated] The array in which to draw the line.
713 realT x0, ///< [in] The x coordinate of the first point
714 realT y0, ///< [in] The y coordinate of the first point
715 realT x1, ///< [in] The x coordinate of the second point
716 realT y1, ///< [in] The y coordinate of the second point
717 realT val ///< [in] The value to set on the pixels in the line
718)
719{
720 if( fabs( x1 - x0 ) <= 1 && fabs( y1 - y0 ) <= 1 )
721 {
722 // If it is a single pixel, plot at the rounded average point.
723 realT xa = 0.5 * ( x0 + x1 ) + 0.5;
724 realT ya = 0.5 * ( y0 + y1 ) + 0.5;
725 if( xa >= 0 && xa < mask.rows() && ya >= 0 && ya < mask.cols() )
726 mask( (int)xa, (int)ya ) = val;
727 return 0;
728 }
729
730 realT _x0, _x1, _y0, _y1;
731
732 realT length = sqrt( pow( x1 - x0, 2 ) + pow( y1 - y0, 2 ) );
733
734 // If X doesn't change enough, we try to do it as a function of y.
735 if( fabs( x1 - x0 ) <= 1 )
736 {
737 if( y0 < y1 )
738 {
739 _x0 = x0;
740 _x1 = x1;
741 _y0 = y0;
742 _y1 = y1;
743 }
744 else
745 {
746 _x0 = x1;
747 _x1 = x0;
748 _y0 = y1;
749 _y1 = y0;
750 }
751
752 realT m = ( _x1 - _x0 ) / ( _y1 - _y0 );
753 realT b = _x0 - m * _y0;
754
755 realT dy = ( _y1 - _y0 ) / ( 2 * length + 1 );
756 for( realT y = _y0; y <= _y1; y += dy )
757 {
758 realT x = m * y + b;
759
760 if( x + 0.5 >= 0 && x + 0.5 < mask.rows() && y + 0.5 >= 0 && y + 0.5 < mask.cols() )
761 mask( (int)( x + 0.5 ), (int)( y + 0.5 ) ) = val;
762 }
763
764 return 0;
765 }
766
767 // Ordinarily draw in x.
768 if( x0 < x1 )
769 {
770 _x0 = x0;
771 _x1 = x1;
772 _y0 = y0;
773 _y1 = y1;
774 }
775 else
776 {
777 _x0 = x1;
778 _x1 = x0;
779 _y0 = y1;
780 _y1 = y0;
781 }
782
783 realT m = ( _y1 - _y0 ) / ( _x1 - _x0 );
784 realT b = _y0 - m * _x0;
785
786 realT dx = ( _x1 - _x0 ) / ( 2 * length + 1 );
787 for( realT x = _x0; x <= _x1; x += dx )
788 {
789 realT y = m * x + b;
790
791 if( x + 0.5 >= 0 && x + 0.5 < mask.rows() && y + 0.5 >= 0 && y + 0.5 < mask.cols() )
792 mask( (int)( x + 0.5 ), (int)( y + 0.5 ) ) = val;
793 }
794}
795
796/// Draw a thick line from one point to another.
797/** Calculates the line connecting the two points and sets the pixels along that line to the
798 * supplied value. Makes the line thick by drawing lines perpindicular to each point with
799 * length equal to the specified width.
800 *
801 * \tparam realT a real floating point type
802 *
803 * \ingroup image_masks
804 */
805template <typename realT>
806int drawLine( eigenImage<realT> &mask, ///< [in.out] [pre-allocated] The array in which to draw the line.
807 realT x0, ///< [in] The x coordinate of the first point
808 realT y0, ///< [in] The y coordinate of the first point
809 realT x1, ///< [in] The x coordinate of the second point
810 realT y1, ///< [in] The y coordinate of the second point
811 realT width, ///< [in] The desired full-width of the line
812 realT val ///< [in] The value to set on the pixels in the line
813)
814{
815 // You can't do this.
816 if( width <= 0 )
817 return -1;
818
819 // If no reason to try this hard, don't.
820 if( width < 1 )
821 return drawLine( mask, x0, y0, x1, y1, val );
822
823 realT _x0, _x1, _y0, _y1;
824
825 // If X doesn't change enough, we try to do it as a function of y.
826 if( fabs( x1 - x0 ) <= 1 )
827 {
828 if( y0 < y1 )
829 {
830 _x0 = x0;
831 _x1 = x1;
832 _y0 = y0;
833 _y1 = y1;
834 }
835 else
836 {
837 _x0 = x1;
838 _x1 = x0;
839 _y0 = y1;
840 _y1 = y0;
841 }
842
843 realT m = ( _x1 - _x0 ) / ( _y1 - _y0 );
844 realT b = _x0 - m * _y0;
845
846 realT dy = ( _y1 - _y0 ) / ( mask.cols() + 1 );
847 for( realT y = _y0; y <= _y1; y += dy )
848 {
849 realT x = m * y + b;
850
851 realT xs, ys, xe, ye; // The start and end point of the perpindicular line.
852
853 realT q1 = atan( m ) + 0.5 * math::pi<realT>();
854 realT cq = cos( q1 );
855 realT sq = sin( q1 );
856
857 xs = x + 0.5 * width * cq;
858 ys = y + 0.5 * width * sq;
859
860 xe = x - 0.5 * width * cq;
861 ye = y - 0.5 * width * sq;
862
863 drawLine( mask, xs, ys, xe, ye, val );
864 }
865
866 return 0;
867 }
868
869 // Ordinarily draw in x.
870 if( x0 < x1 )
871 {
872 _x0 = x0;
873 _x1 = x1;
874 _y0 = y0;
875 _y1 = y1;
876 }
877 else
878 {
879 _x0 = x1;
880 _x1 = x0;
881 _y0 = y1;
882 _y1 = y0;
883 }
884
885 realT m = ( _y1 - _y0 ) / ( _x1 - _x0 );
886 realT b = _y0 - m * _x0;
887
888 realT dx = ( _x1 - _x0 ) / ( mask.rows() + 1 );
889 for( realT x = _x0; x <= _x1; x += dx )
890 {
891 realT y = m * x + b;
892
893 realT xs, ys, xe, ye; // The start and end point of the perpindicular line.
894
895 realT q1 = atan( m ) + 0.5 * math::pi<realT>();
896 realT cq = cos( q1 );
897 realT sq = sin( q1 );
898
899 xs = x + 0.5 * width * cq;
900 ys = y + 0.5 * width * sq;
901
902 xe = x - 0.5 * width * cq;
903 ye = y - 0.5 * width * sq;
904
905 drawLine( mask, xs, ys, xe, ye, val );
906 }
907}
908
909/// Populate a mask based on a typical CCD bleeding pattern.
910/** Masks a circle for saturated pixels, as well as a horizontal bar for bleeding (in both directions if needed).
911 *
912 * \returns 0 on success, -1 otherwise.
913 *
914 * \tparam imT is an Eigen-like 2D array type.
915 *
916 * \ingroup image_masks
917 */
918template <typename imT>
919int ccdBleedMask( imT &im, ///< [out] the mask, on output will be 1/0. Must be allocated prior to call.
920 typename imT::Scalar x0, ///< [in] the x-coordinate of the center of the mask
921 typename imT::Scalar y0, ///< [in] the y-coordinate of the center of the mask
922 typename imT::Scalar rad, ///< [in] the radius of the central circle.
923 typename imT::Scalar height, ///< [in] the half-height of the saturation bar.
924 typename imT::Scalar lwidth, ///< [in] the length of the saturation bar to the left.
925 typename imT::Scalar rwidth ///< [in] the length of the saturation bar to the right.
926)
927{
928 typename imT::Scalar x, y;
929
930 for( int i = 0; i < im.rows(); ++i )
931 {
932 x = i;
933 for( int j = 0; j < im.cols(); ++j )
934 {
935 y = j;
936
937 if( sqrt( pow( x - x0, 2 ) + pow( y - y0, 2 ) ) <= rad )
938 {
939 im( i, j ) = 0;
940 }
941 else if( fabs( y - y0 ) <= height && x >= x0 - lwidth && x <= x0 + rwidth )
942 {
943 im( i, j ) = 0;
944 }
945 else
946 {
947 im( i, j ) = 1;
948 }
949 }
950 }
951
952 return 0;
953}
954
955/// Cut out a region of an image specified by an index-mask.
956/** The output will be a row-image containing the pixel values.
957 *
958 * \tparam imageTout is the output image Eigen-like type.
959 * \tparam imageTin is the input image Eigen-like type.
960 *
961 * \ingroup image_masks
962 */
963template <typename imageTout, typename imageTin>
964void cutImageRegion( imageTout &imout, ///< [out] a row-image containing the pixel values specified by the indices.
965 const imageTin &imin, ///< [in] a 2-D image
966 const std::vector<size_t> &idx, ///< [in] the linear indices of the pixel values
967 bool resize = true ///< [in] [optional] flag controls whether imout is resized. It is if true.
968)
969{
970 if( resize )
971 {
972 imout.resize( idx.size(), 1 );
973 }
974
975 // #pragma omp parallel for schedule(static, 1)
976 for( size_t i = 0; i < idx.size(); ++i )
977 {
978 imout( i ) = imin( idx[i] );
979 }
980}
981
982/// Insert a region of an image specified by an index-mask.
983/** Both the input and the output are row-images containing the pixel values.
984 *
985 * \tparam imageTout is the output image Eigen-like type.
986 * \tparam imageTin is the input image Eigen-like type.
987 *
988 * \ingroup image_masks
989 */
990template <typename imageTout, typename imageTin>
991void insertImageRegion( imageTout imout, /**< [out] a row-image into which the pixel values
992 specified by the indices are inserted. */
993 const imageTin &imin, /**< [in] a row-image containing the pixel values,
994 same size as idx*/
995 const std::vector<size_t> &idx /**< [in] the linear indices of the pixel values */
996)
997{
998 // #pragma omp parallel for schedule(static, 1)
999 for( size_t i = 0; i < idx.size(); ++i )
1000 {
1001 imout( idx[i], 0 ) = imin( i, 0 );
1002 }
1003}
1004
1005/// Rotate a binary mask
1006/** Sets edge pixels to 0 or 1 depending on the interpolation, being below/above 0.5.
1007 */
1008template <typename imageT, typename transformT = cubicConvolTransform<typename imageT::Scalar>>
1009void rotateMask( imageT &rotMask, imageT &mask, typename imageT::Scalar angle )
1010{
1011 imageRotate( rotMask, mask, angle, transformT() );
1012
1013 for( int jj = 0; jj < rotMask.cols(); ++jj )
1014 {
1015 for( int ii = 0; ii < rotMask.rows(); ++ii )
1016 {
1017 if( rotMask( ii, jj ) < 0.5 )
1018 rotMask( ii, jj ) = 0;
1019 else
1020 rotMask( ii, jj ) = 1;
1021 }
1022 }
1023}
1024
1025} // namespace improc
1026} // namespace mx
1027
1028#endif // improc_imageMasks_hpp
Tools for using the eigen library for image processing.
Eigen::Array< scalarT, -1, -1 > eigenImage
Definition of the eigenImage type, which is an alias for Eigen::Array.
constexpr floatT six_fifths()
Return 6/5 in the specified precision.
realT rtod(realT q)
Convert from radians to degrees.
Definition geo.hpp:153
vecT annulusCoordsWorker(const eigenT1 &rIm, const eigenT2 &qIm, typename angleT::realT xcen, typename angleT::realT ycen, typename angleT::realT min_r, typename angleT::realT max_r, typename angleT::realT min_q, typename angleT::realT max_q, eigenT3 *mask=0, typename angleT::realT pixbuf=0)
Get the coordinates of an annular region in an image.
void radiusImage(eigenT &m, typename eigenT::Scalar xc, typename eigenT::Scalar yc, typename eigenT::Scalar scale=1)
Fills in the cells of an Eigen 2D Array with their radius from the center.
void maskCircle(arrayT &m, typename arrayT::Scalar xcen, typename arrayT::Scalar ycen, typename arrayT::Scalar rad, typename arrayT::Scalar val, typename arrayT::Scalar pixbuf=0.5)
Mask a circle in an image.
void cutImageRegion(imageTout &imout, const imageTin &imin, const std::vector< size_t > &idx, bool resize=true)
Cut out a region of an image specified by an index-mask.
void annulusBoundingRect(int &x0, int &y0, int &x1, int &y1, typename angleT::realT xcen, typename angleT::realT ycen, typename angleT::realT min_r, typename angleT::realT max_r, typename angleT::realT min_q, typename angleT::realT max_q, typename angleT::realT pixbuf=0)
Get the coordinates of the bounding rectangle of an annulus.
void maskEllipse(arrayT &m, typename arrayT::Scalar xcen, typename arrayT::Scalar ycen, typename arrayT::Scalar xrad, typename arrayT::Scalar yrad, typename arrayT::Scalar ang, typename arrayT::Scalar val=0, typename arrayT::Scalar pixbuf=0.5)
Mask an ellipse in an image.
int drawLine(eigenImage< realT > &mask, realT x0, realT y0, realT x1, realT y1, realT val)
Draw a thin (1-pixel) line from one point to another.
void applyMask(eigenT &maskedIm, const std::vector< size_t > &idx, typename eigenT::Scalar maskval)
Apply a mask to an image.
int ccdBleedMask(imT &im, typename imT::Scalar x0, typename imT::Scalar y0, typename imT::Scalar rad, typename imT::Scalar height, typename imT::Scalar lwidth, typename imT::Scalar rwidth)
Populate a mask based on a typical CCD bleeding pattern.
void insertImageRegion(imageTout imout, const imageTin &imin, const std::vector< size_t > &idx)
Insert a region of an image specified by an index-mask.
void angleImage(eigenT &m, typename angleT::realT xc, typename angleT::realT yc)
Fills in the cells of an Eigen-like 2D Array with their angle relative to the center.
void maskWedge(arrayT &m, typename arrayT::Scalar xcen, typename arrayT::Scalar ycen, typename arrayT::Scalar angCen, typename arrayT::Scalar angHW, typename arrayT::Scalar val=0)
Mask a wedge in an image.
std::vector< std::vector< int > > annulusCoords(const eigenT1 &rIm, const eigenT2 &qIm, typename angleT::realT xcen, typename angleT::realT ycen, typename angleT::realT min_r, typename angleT::realT max_r, typename angleT::realT min_q, typename angleT::realT max_q, eigenT3 *mask=0, typename angleT::realT pixbuf=0)
Get the array coordinates of an annular region in an image.
void radAngImage(eigenT1 &rIm, eigenT2 &qIm, typename angleT::realT xc, typename angleT::realT yc, typename angleT::realT rscale=1)
Fills in the cells of Eigen-like arrays with their radius amd angle relative to the center.
std::vector< size_t > annulusIndices(const eigenT1 &rIm, const eigenT2 &qIm, typename angleT::realT xcen, typename angleT::realT ycen, typename angleT::realT min_r, typename angleT::realT max_r, typename angleT::realT min_q, typename angleT::realT max_q, eigenT3 *mask=0, typename angleT::realT pixbuf=0)
Get the vector indices of an annular region in an image.
void imageRotate(arrT &transim, const arrT2 &im, floatT dq, transformT trans)
Rotate an image represented as an eigen array.
int reflectImageCoords(int &x1, int &y1, int x0, int y0, realT xc, realT yc)
Reflect pixel coordinates across the given center pixel.
std::vector< size_t > reflectImageIndices(const std::vector< size_t > &idxi, int w, int h, realT xc, realT yc)
Reflect vector indices across the given center pixel.
void rotateMask(imageT &rotMask, imageT &mask, typename imageT::Scalar angle)
Rotate a binary mask.
Image interpolation and transformation.
The mxlib c++ namespace.
Definition mxError.hpp:106