Skip to content

Commit

Permalink
Separated bad pixel median interpolator from the pipeline logic. Now,…
Browse files Browse the repository at this point in the history
… the pipeline API uses the bad pixel median interpolator.
  • Loading branch information
Carsten Schmitt committed May 3, 2024
1 parent c1bf689 commit b3f17e7
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 90 deletions.
152 changes: 152 additions & 0 deletions source/focus_finder/common/include/bad_pixel_median_interpolator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*****************************************************************************
*
* FoFi - A free, automatic telescope focus finder.
*
* Copyright(C) 2019 Carsten Schmitt <c [at] lost-infinity.com>
*
* More info on https://www.lost-infinity.com
*
* This program is free software ; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
****************************************************************************/

#ifndef STARMATH_BAD_PIXEL_MEDIAN_INTERPOLATOR_H
#define STARMATH_BAD_PIXEL_MEDIAN_INTERPOLATOR_H STARMATH_BAD_PIXEL_MEDIAN_INTERPOLATOR_H

/**
* Usage:
*
* BadPixelMedianInterpolatorT badPixelMedianInterpolator();
* auto outputImage = badPixelMedianInterpolator.interpolate(inputImage);
*
*/
class BadPixelMedianInterpolatorT {
public:
/**
* TODO: Should this be here?
*/
struct ThresholdDirectionT {
enum TypeE {
POSITIVE, // Hot pixels
NEGATIVE, // Cold pixels
BOTH, // Hot- and cold pixels
_Count
};

static const char *asStr(const TypeE &inType) {
switch (inType) {
case POSITIVE:
return "POSITIVE";
case NEGATIVE:
return "NEGATIVE";
case BOTH:
return "BOTH";
default:
return "<?>";
}
}

MAC_AS_TYPE(Type, E, _Count);
};


/**
*
*/
explicit BadPixelMedianInterpolatorT(float absoluteDetectionThreshold = 500, unsigned int filterCoreSize = 3, ThresholdDirectionT::TypeE thresholdDirection = ThresholdDirectionT::BOTH) : absoluteDetectionThreshold_(absoluteDetectionThreshold), filterCoreSize_(filterCoreSize), thresholdDirection_(thresholdDirection) {
}


/**
*
*/
template<typename ImageType>
std::shared_ptr<cimg_library::CImg<ImageType>> interpolate(const cimg_library::CImg<ImageType> &inputImageRef) {

// See https://cimg.eu/reference/loops_Using.html
cimg_library::CImg<ImageType> neighbourhood(filterCoreSize_, filterCoreSize_);

auto result_image = std::make_shared<cimg_library::CImg<ImageType>>(inputImageRef);

switch (filterCoreSize_) {
case 3: {
cimg_for3x3(inputImageRef, x, y, 0, 0, neighbourhood, ImageType) {
(*result_image)(x,y) = interpolateInternal(inputImageRef(x,y), neighbourhood, absoluteDetectionThreshold_, thresholdDirection_);
}
break;
}
case 5: {
cimg_for5x5(inputImageRef, x, y, 0, 0, neighbourhood, ImageType) {
(*result_image)(x,y) = interpolateInternal(inputImageRef(x,y), neighbourhood, absoluteDetectionThreshold_, thresholdDirection_);
}
break;
}
case 7: {
cimg_for7x7(inputImageRef, x, y, 0, 0, neighbourhood, ImageType) {
(*result_image)(x,y) = interpolateInternal(inputImageRef(x,y), neighbourhood, absoluteDetectionThreshold_, thresholdDirection_);
}
break;
}
case 9: {
cimg_for9x9(inputImageRef, x, y, 0, 0, neighbourhood, ImageType) {
(*result_image)(x,y) = interpolateInternal(inputImageRef(x,y), neighbourhood, absoluteDetectionThreshold_, thresholdDirection_);
}
break;
}
//default:
// TODO: throw...

}
return result_image;
}



private:
float absoluteDetectionThreshold_;
unsigned int filterCoreSize_;
ThresholdDirectionT::TypeE thresholdDirection_;

/**
* TODO: Move to .cpp file? Or solve it via a #define?
*/
template<typename ImageType>
ImageType interpolateInternal(ImageType pixelValue, const cimg_library::CImg<ImageType> & neighbourhood, float threshold, ThresholdDirectionT::TypeE thresholdDirection) {
ImageType med = neighbourhood.median();

bool wantInterpolation = false;

switch(thresholdDirection) {
case ThresholdDirectionT::POSITIVE:
wantInterpolation = ((pixelValue - med) > threshold);
break;

case ThresholdDirectionT::NEGATIVE:
wantInterpolation = ((med - pixelValue) > threshold);
break;

case ThresholdDirectionT::BOTH:
wantInterpolation = (std::abs(pixelValue - med) > threshold);
break;

//default:
// TODO: throw...
}
return (wantInterpolation ? med : pixelValue);
}

};

#endif /*STARMATH_BAD_PIXEL_MEDIAN_INTERPOLATOR_H*/
Original file line number Diff line number Diff line change
Expand Up @@ -28,67 +28,12 @@
#include <range/v3/view/transform.hpp>

#include "../../image.h"
#include "../../bad_pixel_median_interpolator.h"

#define STARMATH_INTERPOLATE_BAD_PIXELS_DEBUG 1

namespace starmath::pipeline {

/**
* TODO: Should this be here?
*/
struct ThresholdDirectionT {
enum TypeE {
POSITIVE, // Hot pixels
NEGATIVE, // Cold pixels
BOTH, // Hot- and cold pixels
_Count
};

static const char *asStr(const TypeE &inType) {
switch (inType) {
case POSITIVE:
return "POSITIVE";
case NEGATIVE:
return "NEGATIVE";
case BOTH:
return "BOTH";
default:
return "<?>";
}
}

MAC_AS_TYPE(Type, E, _Count);
};



/**
* TODO: Move to .cpp file? Or solve it via a #define?
*/
template<typename ImageType>
ImageType interpolateInternal(ImageType pixelValue, const cimg_library::CImg<ImageType> & neighbourhood, float threshold, ThresholdDirectionT::TypeE thresholdDirection) {
ImageType med = neighbourhood.median();

bool wantInterpolation = false;

switch(thresholdDirection) {
case ThresholdDirectionT::POSITIVE:
wantInterpolation = ((pixelValue - med) > threshold);
break;

case ThresholdDirectionT::NEGATIVE:
wantInterpolation = ((med - pixelValue) > threshold);
break;

case ThresholdDirectionT::BOTH:
wantInterpolation = (std::abs(pixelValue - med) > threshold);
break;

//default:
// TODO: throw...
}
return (wantInterpolation ? med : pixelValue);
}

/**
* If the potential "bad" pixel value deviates more than this "factor" from the mean
Expand All @@ -110,47 +55,16 @@ namespace starmath::pipeline {
*/
template<typename ImageType=float>
auto
interpolate_bad_pixels(float absoluteDetectionThreshold = 500, unsigned int filterCoreSize = 3, ThresholdDirectionT::TypeE thresholdDirection = ThresholdDirectionT::BOTH) {
interpolate_bad_pixels(float absoluteDetectionThreshold = 500, unsigned int filterCoreSize = 3, BadPixelMedianInterpolatorT::ThresholdDirectionT::TypeE thresholdDirection = BadPixelMedianInterpolatorT::ThresholdDirectionT::BOTH) {
return ranges::views::transform(
[=](const std::shared_ptr<cimg_library::CImg<ImageType> > &image) {
const cimg_library::CImg<ImageType> &inputImageRef = *image;

DEBUG_IMAGE_DISPLAY(inputImageRef, "interpolate_bad_pixels_in", STARMATH_INTERPOLATE_BAD_PIXELS_DEBUG);

auto result_image = std::make_shared<cimg_library::CImg<ImageType>>(inputImageRef);

// See https://cimg.eu/reference/loops_Using.html
cimg_library::CImg<ImageType> neighbourhood(filterCoreSize, filterCoreSize);
BadPixelMedianInterpolatorT badPixelMedianInterpolator(absoluteDetectionThreshold, filterCoreSize, thresholdDirection);

switch (filterCoreSize) {
case 3: {
cimg_for3x3(inputImageRef, x, y, 0, 0, neighbourhood, ImageType) {
(*result_image)(x,y) = interpolateInternal(inputImageRef(x,y), neighbourhood, absoluteDetectionThreshold, thresholdDirection);
}
break;
}
case 5: {
cimg_for5x5(inputImageRef, x, y, 0, 0, neighbourhood, ImageType) {
(*result_image)(x,y) = interpolateInternal(inputImageRef(x,y), neighbourhood, absoluteDetectionThreshold, thresholdDirection);
}
break;
}
case 7: {
cimg_for7x7(inputImageRef, x, y, 0, 0, neighbourhood, ImageType) {
(*result_image)(x,y) = interpolateInternal(inputImageRef(x,y), neighbourhood, absoluteDetectionThreshold, thresholdDirection);
}
break;
}
case 9: {
cimg_for9x9(inputImageRef, x, y, 0, 0, neighbourhood, ImageType) {
(*result_image)(x,y) = interpolateInternal(inputImageRef(x,y), neighbourhood, absoluteDetectionThreshold, thresholdDirection);
}
break;
}
//default:
// TODO: throw...

}
auto result_image = badPixelMedianInterpolator.interpolate(inputImageRef);

DEBUG_IMAGE_DISPLAY(*result_image, "interpolate_bad_pixels_out", STARMATH_INTERPOLATE_BAD_PIXELS_DEBUG);

Expand Down
5 changes: 5 additions & 0 deletions source/focus_finder/common/include/star_cluster_algorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
*
****************************************************************************/

#ifndef STARMATH_STAR_CLUSTER_ALGORITHM_H
#define STARMATH_STAR_CLUSTER_ALGORITHM_H STARMATH_STAR_CLUSTER_ALGORITHM_H

#include <list>
#include <vector>
#include <set>
Expand Down Expand Up @@ -82,3 +85,5 @@ class StarClusterAlgorithmT {

std::list<PixelClusterT> cluster(const ImageT &inImg);
};

#endif /*STARMATH_STAR_CLUSTER_ALGORITHM_H*/
1 change: 1 addition & 0 deletions source/focus_finder/tests/starmath-tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ set(sources
../../common/max_entropy_thresholding_algorithm.cpp
../../common/otsu_thresholding_algorithm.cpp
../../common/mean_thresholding_algorithm.cpp
../../common/include/bad_pixel_median_interpolator.h
star_cluster_algorithm_tests.cpp image_slicer_tests.cpp point_tests.cpp hfd_tests.cpp centroid_tests.cpp image_reader_tests.cpp thresholder_tests.cpp real_world_star_image_tests.cpp ../../common/include/pipeline/view/images.h ../../common/include/pipeline/view/subtract_background.h ../../common/include/pipeline/view/scale.h ../../common/include/pipeline/view/center_on_star.h ../../common/include/pipeline/view/crop.h ../../common/include/pipeline/action/average.h ../../common/include/pipeline/view/subtract.h ../../common/include/pipeline/view/remove_nans.h ../../common/include/pipeline/view/files.h pipeline_files_tests.cpp pipeline_images_tests.cpp pipeline_subtract_background_tests.cpp pipeline_crop_tests.cpp pipeline_center_on_star_tests.cpp pipeline_scale_tests.cpp pipeline_average_tests.cpp pipeline_subtract_tests.cpp pipeline_divide_by_tests.cpp rect_tests.cpp pipeline_add_tests.cpp ../../common/include/pipeline/view/add.h ../../common/include/pipeline/view/interpolate_bad_pixels.h ../../common/include/pipeline/view/multiply_by.h pipeline_multiply_by_tests.cpp pipeline_real_world_tests.cpp defect_pixel_filter_tests.cpp)

# create an executable, which instantiates a runner from
Expand Down

0 comments on commit b3f17e7

Please sign in to comment.