8#ifndef __imageFilters_hpp__
9#define __imageFilters_hpp__
14#include "../math/gslInterpolator.hpp"
15#include "../math/vectorUtils.hpp"
16#include "../math/geo.hpp"
66template <
typename _arrayT,
size_t _kernW = 4,
class _verboseT = verbose::d>
69 typedef _arrayT arrayT;
70 typedef typename _arrayT::Scalar arithT;
71 static const int kernW = _kernW;
73 typedef _verboseT verboseT;
79 explicit gaussKernel( arithT fwhm )
83 int w = kernW * _fwhm;
88 kernel.resize( w, w );
90 arithT kcen = 0.5 * ( w - 1.0 );
92 arithT sig2 = _fwhm / 2.354820045030949327;
96 for(
int i = 0; i < w; ++i )
98 for(
int j = 0; j < w; ++j )
100 r2 = pow( i - kcen, 2 ) + pow( j - kcen, 2 );
101 kernel( i, j ) = exp( -r2 / ( 2.0 * sig2 ) );
105 kernel /= kernel.sum();
110 return _kernW * _fwhm;
119 static_cast<void>( x );
120 static_cast<void>( y );
122 kernelArray = kernel;
133template <
typename _arrayT,
size_t _kernW = 2,
class _verboseT = verbose::d>
136 typedef _arrayT arrayT;
137 typedef typename _arrayT::Scalar arithT;
139 static const int kernW = _kernW;
141 typedef _verboseT verboseT;
167 maxAz = fabs( maxAz );
180 arithT mx1 = kernW * ( (int)( fabs(
m_azWidth * sin( qmax ) ) + fabs(
m_radWidth * cos( qmax ) ) ) + 1 );
183 arithT mx2 = kernW * ( (int)( fabs(
m_azWidth * cos( qmax ) ) + fabs(
m_radWidth * sin( qmax ) ) ) + 1 );
185 m_maxWidth = 0.5 * std::max( mx1, mx2 );
198 arithT rad0 = sqrt( (arithT)( x * x + y * y ) );
200 arithT sinq = y / rad0;
201 arithT cosq = x / rad0;
207 q = atan2( sinq, cosq );
213 if( w > m_maxWidth * 2 )
216 std::format(
"Width bigger than 2*maxWidth. "
217 "This is a bug. Details: "
218 "|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|",
232 if( h > m_maxWidth * 2 )
235 std::format(
"Height bigger than 2*maxWidth. "
236 "This is a bug. Details: "
237 "|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|",
251 kernel.resize( w, h );
253 arithT xcen = 0.5 * ( w - 1.0 );
254 arithT ycen = 0.5 * ( h - 1.0 );
258 arithT sinq2, cosq2, sindq, q2, dq;
259 for(
int j = 0; j < h; ++j )
262 for(
int i = 0; i < w; ++i )
265 rad = sqrt( pow( xP - xcen, 2 ) + pow( yP - ycen, 2 ) );
266 radP = sqrt( pow( x + xP - xcen, 2 ) + pow( y + yP - ycen, 2 ) );
274 sinq2 = ( yP - ycen ) / rad;
275 cosq2 = ( xP - xcen ) / rad;
277 sindq = sinq2 * cosq - cosq2 * sinq;
282 q2 = atan2( y + yP - ycen, x + xP - xcen );
297 arithT ksum = kernel.sum();
301 std::format(
"kernel sum 0 at {},{}", x, y ) );
314template <
class kernelT>
317 typedef kernelT::arrayT arrayT;
318 typedef kernelT::arrayT::Scalar arithT;
319 typedef kernelT::verboseT verboseT;
330 std::vector<arrayT> m_kernels;
332 precalKernel() =
delete;
340 : m_kernel( kernel ), m_rows( rows ), m_cols( cols ), m_xcen( xcen ), m_ycen( ycen )
342 m_maxWidth = kernel.maxWidth();
344 m_kernels.resize( m_rows * m_cols );
346 for(
int cc = 0; cc < m_cols; ++cc )
348 for(
int rr = 0; rr < m_rows; ++rr )
350 m_kernel.setKernel( rr - xcen, cc - ycen, m_kernels[n] );
366 size_t n = ( y + m_ycen ) * m_rows + ( x + m_xcen );
368 if( m_kernels.size() == 0 )
373 if( n > m_kernels.size() - 1 )
378 kernel = m_kernels[n];
394template <
typename imageOutT,
typename imageInT,
typename kernelT>
397 const kernelT &kernel,
403 typedef typename kernelT::verboseT verboseT;
405 fim.resize( im.rows(), im.cols() );
407 float xcen = 0.5 * ( im.rows() - 1 );
408 float ycen = 0.5 * ( im.cols() - 1 );
412 maxr = 0.5 * im.rows() - kernel.maxWidth();
415 int mini = 0.5 * im.rows() - maxr;
416 int maxi = 0.5 * im.rows() + maxr;
417 int minj = 0.5 * im.cols() - maxr;
418 int maxj = 0.5 * im.cols() + maxr;
420 typename kernelT::arrayT kernelArray;
425 #pragma omp parallel private( kernelArray )
427 int im_i, im_j, im_p, im_q;
428 int kern_i, kern_j, kern_p, kern_q;
429 typename imageOutT::Scalar norm;
435 for(
int i = 0; i < im.rows(); ++i )
442 for(
int j = 0; j < im.cols(); ++j )
449 if( ( i >= mini && i < maxi ) && ( j >= minj && j < maxj ) )
451 errc = kernel.setKernel( i - xcen, j - ycen, kernelArray );
453 fim( i, j ) = ( im.block( i - 0.5 * ( kernelArray.rows() - 1 ),
454 j - 0.5 * ( kernelArray.cols() - 1 ),
456 kernelArray.cols() ) *
462 errc = kernel.setKernel( i - xcen, j - ycen, kernelArray );
464 im_i = i - 0.5 * ( kernelArray.rows() - 1 );
468 im_j = j - 0.5 * ( kernelArray.cols() - 1 );
472 im_p = im.rows() - im_i;
473 if( im_p > kernelArray.rows() )
474 im_p = kernelArray.rows();
476 im_q = im.cols() - im_j;
477 if( im_q > kernelArray.cols() )
478 im_q = kernelArray.cols();
480 kern_i = 0.5 * ( kernelArray.rows() - 1 ) - i;
484 kern_j = 0.5 * ( kernelArray.cols() - 1 ) - j;
488 kern_p = kernelArray.rows() - kern_i;
489 if( kern_p > kernelArray.rows() )
490 kern_p = kernelArray.rows();
492 kern_q = kernelArray.cols() - kern_j;
493 if( kern_q > kernelArray.cols() )
494 kern_q = kernelArray.cols();
502 norm = kernelArray.block( kern_i, kern_j, kern_p, kern_q ).sum();
505 ( im.block( im_i, im_j, kern_p, kern_q ) * kernelArray.block( kern_i, kern_j, kern_p, kern_q ) )
508 if( !std::isfinite( fim( i, j ) ) )
539template <
typename imageOutT,
typename imageInT,
typename kernelT>
542 const kernelT &kernel,
547 fim.resize( im.rows(), im.cols() );
549 float xcen = 0.5 * ( im.rows() - 1 );
550 float ycen = 0.5 * ( im.cols() - 1 );
553 maxr = 0.5 * im.rows() - kernel.maxWidth();
555 int mini = 0.5 * im.rows() - maxr;
556 int maxi = 0.5 * im.rows() + maxr;
557 int minj = 0.5 * im.cols() - maxr;
558 int maxj = 0.5 * im.cols() + maxr;
560 typename kernelT::arrayT kernelArray;
563 #pragma omp parallel private( kernelArray )
565 int im_i, im_j, im_p, im_q;
566 int kern_i, kern_j, kern_p, kern_q;
567 typename imageOutT::Scalar norm;
569 std::vector<typename imageOutT::Scalar> pixels;
573 for(
int i = 0; i < im.rows(); ++i )
575 for(
int j = 0; j < im.cols(); ++j )
577 if( ( i >= mini && i < maxi ) && ( j >= minj && j < maxj ) )
579 kernel.setKernel( i - xcen, j - ycen, kernelArray );
582 for(
int cc = 0; cc < kernelArray.cols(); ++cc )
584 for(
int rr = 0; rr < kernelArray.rows(); ++rr )
586 if( kernelArray( rr, cc ) != 0 )
587 pixels.push_back( im.block( i - 0.5 * ( kernelArray.rows() - 1 ),
588 j - 0.5 * ( kernelArray.cols() - 1 ),
590 kernelArray.cols() )( rr, cc ) );
603 kernel.setKernel( i - xcen, j - ycen, kernelArray );
605 im_i = i - 0.5 * ( kernelArray.rows() - 1 );
609 im_j = j - 0.5 * ( kernelArray.cols() - 1 );
613 im_p = im.rows() - im_i;
614 if( im_p > kernelArray.rows() )
615 im_p = kernelArray.rows();
617 im_q = im.cols() - im_j;
618 if( im_q > kernelArray.cols() )
619 im_q = kernelArray.cols();
621 kern_i = 0.5 * ( kernelArray.rows() - 1 ) - i;
625 kern_j = 0.5 * ( kernelArray.cols() - 1 ) - j;
629 kern_p = kernelArray.rows() - kern_i;
630 if( kern_p > kernelArray.rows() )
631 kern_p = kernelArray.rows();
633 kern_q = kernelArray.cols() - kern_j;
634 if( kern_q > kernelArray.cols() )
635 kern_q = kernelArray.cols();
643 norm = kernelArray.block( kern_i, kern_j, kern_p, kern_q ).sum();
646 for(
int cc = 0; cc < kern_q; ++cc )
648 for(
int rr = 0; rr < kern_p; ++rr )
650 if( kernelArray.block( kern_i, kern_j, kern_p, kern_q )( rr, cc ) != 0 )
652 pixels.push_back( im.block( im_i, im_j, kern_p, kern_q )( rr, cc ) );
687template <
typename imageTout,
typename imageTin>
690 const imageTin &imIn,
692 bool rejectMinMax =
false
695 typedef typename imageTout::Scalar scalarT;
697 int buff = 0.5 * meanFullWidth;
699 int nPix = meanFullWidth * meanFullWidth;
704 for(
int jj = buff; jj < imIn.cols() - buff; ++jj )
706 for(
int ii = buff; ii < imIn.rows() - buff; ++ii )
711 for(
int ll = jj - buff; ll < jj + buff + 1; ++ll )
713 for(
int kk = ii - buff; kk < ii + buff + 1; ++kk )
716 sum += imIn( kk, ll );
717 if( imIn( kk, ll ) > max )
718 max = imIn( kk, ll );
719 if( imIn( kk, ll ) < min )
720 min = imIn( kk, ll );
723 imOut( ii, jj ) = ( sum - max - min ) / nPix;
729 for(
int jj = buff; jj < imIn.cols() - buff; ++jj )
731 for(
int ii = buff; ii < imIn.rows() - buff; ++ii )
734 for(
int ll = jj - buff; ll < jj + buff + 1; ++ll )
736 for(
int kk = ii - buff; kk < ii + buff + 1; ++kk )
738 sum += imIn( kk, ll );
741 imOut( ii, jj ) = sum / nPix;
774template <
typename imageTout,
typename imageTin>
778 typename imageTout::Scalar &pMax,
779 const imageTin &imIn,
781 bool rejectMinMax =
false
784 typedef typename imageTout::Scalar scalarT;
786 int buff = 0.5 * meanFullWidth;
788 int nPix = meanFullWidth * meanFullWidth;
790 pMax = std::numeric_limits<scalarT>::lowest();
795 for(
int jj = buff; jj < imIn.cols() - buff; ++jj )
797 for(
int ii = buff; ii < imIn.rows() - buff; ++ii )
802 for(
int ll = jj - buff; ll < jj + buff + 1; ++ll )
804 for(
int kk = ii - buff; kk < ii + buff + 1; ++kk )
807 sum += imIn( kk, ll );
808 if( imIn( kk, ll ) > max )
809 max = imIn( kk, ll );
810 if( imIn( kk, ll ) < min )
811 min = imIn( kk, ll );
814 imOut( ii, jj ) = ( sum - max - min ) / nPix;
815 if( imOut( ii, jj ) > pMax )
817 pMax = imOut( ii, jj );
826 for(
int jj = buff; jj < imIn.cols() - buff; ++jj )
828 for(
int ii = buff; ii < imIn.rows() - buff; ++ii )
831 for(
int ll = jj - buff; ll < jj + buff + 1; ++ll )
833 for(
int kk = ii - buff; kk < ii + buff + 1; ++kk )
835 sum += imIn( kk, ll );
838 imOut( ii, jj ) = sum / nPix;
839 if( imOut( ii, jj ) > pMax )
841 pMax = imOut( ii, jj );
872template <
typename imageTout,
typename imageTin>
877 typename imageTout::Scalar &pMax,
878 const imageTin &imIn,
882 typedef typename imageTout::Scalar scalarT;
884 int buff = 0.5 * medianFullWidth;
886 std::vector<scalarT> pixs( medianFullWidth * medianFullWidth );
888 pMax = std::numeric_limits<scalarT>::lowest();
890 for(
int jj = buff; jj < imIn.cols() - buff; ++jj )
892 for(
int ii = buff; ii < imIn.rows() - buff; ++ii )
895 for(
int ll = jj - buff; ll < jj + buff + 1; ++ll )
897 for(
int kk = ii - buff; kk < ii + buff + 1; ++kk )
899 pixs[n] = imIn( kk, ll );
905 if( imOut( ii, jj ) > pMax )
907 pMax = imOut( ii, jj );
934template <
typename imageTout,
typename imageTin>
937 const imageTin &imIn,
942 typename imageTout::Scalar pMax;
943 return medianSmooth( imOut, xMax, yMax, pMax, imIn, medianFullWidth );
946template <
typename eigenImT>
951 typedef typename eigenImT::Scalar realT;
953 std::vector<realT> edge( 2 * ncols );
954 for(
int rr = 0; rr < im.rows(); ++rr )
956 for(
int cc = 0; cc < ncols; ++cc )
958 edge[cc] = im( rr, cc );
961 for(
int cc = 0; cc < ncols; ++cc )
963 edge[ncols + cc] = im( rr, im.cols() - ncols + cc );
973template <
typename eigenImT>
978 typedef typename eigenImT::Scalar realT;
980 std::vector<realT> edge( 2 * nrows );
981 for(
int cc = 0; cc < im.cols(); ++cc )
983 for(
int rr = 0; rr < nrows; ++rr )
985 edge[rr] = im( rr, cc );
988 for(
int rr = 0; rr < nrows; ++rr )
990 edge[nrows + rr] = im( im.rows() - nrows + rr, cc );
1002template <
typename floatT>
1009template <
typename floatT>
1012 bool operator()( radval<floatT> rv1, radval<floatT> rv2 )
1014 return ( rv1.r < rv2.r );
1018template <
typename floatT>
1021 bool operator()( radval<floatT> rv1, radval<floatT> rv2 )
1023 return ( rv1.v < rv2.v );
1038template <
typename vecT,
typename eigenImT1,
typename eigenImT2,
typename eigenImT3>
1042 const eigenImT1 &im,
1043 const eigenImT2 &radim,
1044 const eigenImT3 *mask,
1047 typename eigenImT1::Scalar minr = 0 )
1049 typedef typename eigenImT1::Scalar floatT;
1051 int dim1 = im.rows();
1052 int dim2 = im.cols();
1068 std::vector<radval<floatT>> rv( nPix );
1072 for(
int c = 0; c < im.cols(); ++c )
1074 for(
int r = 0; r < im.rows(); ++r )
1078 if( ( *mask )( r, c ) == 0 )
1082 rv[i].r = radim( r, c );
1083 rv[i].v = im( r, c );
1088 sort( rv.begin(), rv.end(), radvalRadComp<floatT>() );
1104 floatT r1 = minr + dr;
1111 while( rv[i1].r < r0 )
1114 while( rv[i2].r <= r1 )
1120 for(
int in = i1; in < i2; ++in )
1126 n = 0.5 * ( i2 - i1 );
1128 std::nth_element( rv.begin() + i1, rv.begin() + i1 + n, rv.begin() + i2, radvalValComp<floatT>() );
1130 med = ( rv.begin() + i1 + n )->v;
1133 if( ( i2 - i1 ) % 2 == 0 )
1137 ( med + ( *std::max_element( rv.begin() + i1, rv.begin() + i1 + n, radvalValComp<floatT>() ) ).v );
1141 rad.push_back( .5 * ( r0 + r1 ) );
1142 prof.push_back( med );
1162template <
typename vecT,
typename eigenImT1,
typename eigenImT2>
1166 const eigenImT1 &im,
1167 const eigenImT2 &mask,
1172 radim.resize( im.cols(), im.rows() );
1176 radprof( rad, prof, im, radim, &mask, mean );
1190template <
typename vecT,
typename eigenImT1>
1194 const eigenImT1 &im,
1199 radim.resize( im.cols(), im.rows() );
1217template <
typename radprofT,
typename eigenImT1,
typename eigenImT2,
typename eigenImT3>
1220 const eigenImT2 &rad,
1221 const eigenImT3 *mask,
1229 std::vector<double> med_r, med_v;
1231 radprof( med_r, med_v, im, rad, mask );
1234 radprofIm.resize( im.rows(), im.cols() );
1238 for(
int c = 0; c < im.cols(); ++c )
1240 for(
int r = 0; r < im.rows(); ++r )
1244 if( ( *mask )( r, c ) == 0 )
1246 radprofIm( r, c ) = 0;
1251 radprofIm( r, c ) = interp( ( (
double)rad( r, c ) ) );
1253 im( r, c ) -= radprofIm( r, c );
1267template <
typename radprofT,
typename eigenImT>
1271 bool subtract =
false,
1276 rad.resize( im.rows(), im.cols() );
1294template <
typename eigenImT,
typename eigenImT1,
typename eigenImT2,
typename eigenImT3>
1296 const eigenImT1 &im,
1297 const eigenImT2 &rad,
1298 const eigenImT3 &mask,
1299 typename eigenImT::Scalar minRad,
1300 typename eigenImT::Scalar maxRad,
1305 typedef typename eigenImT::Scalar floatT;
1307 int dim1 = im.cols();
1308 int dim2 = im.rows();
1310 floatT mr = rad.maxCoeff();
1313 std::vector<radval<floatT>> rv( dim1 * dim2 );
1315 for(
int i = 0; i < rv.size(); ++i )
1317 if( mask( i ) == 0 )
1324 sort( rv.begin(), rv.end(), radvalRadComp<floatT>() );
1334 std::vector<double> std_r, std_v;
1338 while( rv[i1].r < r0 && i1 < rv.size() )
1342 if( i1 == rv.size() )
1348 while( rv[i2].r <= r1 && i2 < rv.size() )
1350 n = 0.5 * ( i2 - i1 );
1352 std::vector<double> vals;
1354 for(
int i = i1; i < i2; ++i )
1356 vals.push_back( rv[i].v );
1359 std_r.push_back( .5 * ( r0 + r1 ) );
1368 stdIm.resize( dim1, dim2 );
1371 for(
int i = 0; i < dim1; ++i )
1373 for(
int j = 0; j < dim2; ++j )
1375 if( rad( i, j ) < minRad || rad( i, j ) > maxRad )
1381 stdIm( i, j ) = interp( ( (
double)rad( i, j ) ) );
1383 stdIm( i, j ) = im( i, j ) / stdIm( i, j );
1400template <
typename eigenImT,
typename eigenImT1,
typename eigenImT2>
1402 const eigenImT1 &im,
1403 const eigenImT2 &mask,
1404 typename eigenImT::Scalar minRad,
1405 typename eigenImT::Scalar maxRad,
1410 int dim1 = im.cols();
1411 int dim2 = im.rows();
1414 rad.resize( dim1, dim2 );
1417 stddevImage( stdIm, im, rad, mask, minRad, maxRad, divide );
1429template <
typename eigenCubeT,
typename eigenCubeT1,
typename eigenCubeT2,
typename radT1,
typename radT2>
1432 const eigenCubeT1 &imc,
1433 const eigenCubeT2 &maskCube,
1440 int dim1 = imc.cols();
1441 int dim2 = imc.rows();
1443 typename eigenCubeT::Scalar minRadF = minRad;
1444 typename eigenCubeT::Scalar maxRadF = maxRad;
1447 rad.resize( dim1, dim2 );
1451 stdImc.resize( imc.rows(), imc.cols(), imc.planes() );
1454 for(
int i = 0; i < imc.planes(); ++i )
1458 im = imc.image( i );
1459 mask = maskCube.image( i );
1461 stddevImage( stdIm, im, rad, mask, minRadF, maxRadF, divide );
1463 stdImc.image( i ) = stdIm;
Class to manage interpolation using the GSL interpolation library.
Eigen::Array< scalarT, -1, -1 > eigenImage
Definition of the eigenImage type, which is an alias for Eigen::Array.
error_t
The mxlib error codes.
@ noerror
No error has occurred.
@ sizeerr
A size was invalid or calculated incorrectly.
@ invalidconfig
A config setting was invalid.
@ invalidarg
An argument was invalid.
constexpr floatT six_fifths()
Return 6/5 in the specified precision.
realT dtor(realT q)
Convert from degrees to radians.
int meanSmooth(imageTout &imOut, const imageTin &imIn, int meanFullWidth, bool rejectMinMax=false)
Smooth an image using the mean in a rectangular box, optionally rejecting the highest and lowest valu...
int medianSmooth(imageTout &imOut, int &xMax, int &yMax, typename imageTout::Scalar &pMax, const imageTin &imIn, int medianFullWidth)
Smooth an image using the median in a rectangular box. Also Determines the location and value of the ...
error_t filterImage(imageOutT &fim, imageInT im, const kernelT &kernel, int maxr=0)
Filter an image with a mean kernel.
void medianFilterImage(imageOutT &fim, imageInT im, const kernelT &kernel, int maxr=0, int maxrproc=1)
Filter an image with a median kernel.
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 radprof(vecT &rad, vecT &prof, const eigenImT1 &im, const eigenImT2 &radim, const eigenImT3 *mask, bool mean=false, typename eigenImT1::Scalar minr=0)
Calculate the the radial profile.
void radprofim(radprofT &radprofIm, eigenImT1 &im, const eigenImT2 &rad, const eigenImT3 *mask, bool subtract, bool mean=false)
Form a radial profile image, and optionally subtract it from the input.
void stddevImage(eigenImT &stdIm, const eigenImT1 &im, const eigenImT2 &rad, const eigenImT3 &mask, typename eigenImT::Scalar minRad, typename eigenImT::Scalar maxRad, bool divide)
Form a standard deviation image, and optionally divide the input by it to form a S/N map.
void stddevImageCube(eigenCubeT &stdImc, const eigenCubeT1 &imc, const eigenCubeT2 &maskCube, radT1 minRad, radT2 maxRad, bool divide=false)
Form a standard deviation image for each imamge in a cube, and optionally divide the input by it form...
vectorT::value_type vectorMedianInPlace(vectorT &vec)
Calculate median of a vector in-place, altering the vector.
valueT vectorVariance(const valueT *vec, size_t sz, valueT mean)
Calculate the variance of a vector relative to a supplied mean value.
vectorT::value_type vectorMedian(const vectorT &vec, vectorT *work=0)
Calculate median of a vector, leaving the vector unaltered.
void colEdgeMedSubtract(eigenImT &im, int nrows)
void rowEdgeMedSubtract(eigenImT &im, int ncols)
Declares and defines functions to work with image masks.
Azimuthally variable boxcar kernel.
azBoxKernel(arithT radWidth, arithT azWidth, arithT maxAz)
arithT m_radWidth
the half-width of the averaging box, in the radial direction, in pixels.
void setMaxWidth()
Sets the max width based on the configured az and rad widths.
arithT m_maxAz
the maximum half-width of the averging box in the azimuthal direction, in degrees....
azBoxKernel(arithT radWidth, arithT azWidth)
error_t setKernel(arithT x, arithT y, arrayT &kernel) const
arithT m_azWidth
the half-width of the averaging box, in the azimuthal direction, in pixels.
A kernel that is pre-calculated for the entire image, useful for repeated applications.
precalcKernel(const kernelT &kernel, uint32_t rows, uint32_t cols, arithT xcen, arithT ycen)
error_t setKernel(arithT x, arithT y, arrayT &kernel) const