diff --git a/config/hyperion.config.json b/config/hyperion.config.json index 34f485cf..0f8404a9 100644 --- a/config/hyperion.config.json +++ b/config/hyperion.config.json @@ -355,10 +355,21 @@ /// The black border configuration, contains the following items: /// * enable : true if the detector should be activated /// * threshold : Value below which a pixel is regarded as black (value between 0.0 and 1.0) - "blackborderdetector" : + /// * unknownFrameCnt : Number of frames without any detection before the border is set to 0 (default 600) - optional + /// * borderFrameCnt : Number of frames before a consistent detected border gets set (default 50) - optional + /// * maxInconsistentCnt : Number of inconsistent frames that are ignored before a new border gets a chance to proof consistency - optional + /// * blurRemoveCnt : Number of pixels that get removed from the detected border to cut away blur (default 1) - optional + /// * mode : Border detection mode (values "default","classic","osd") - optional + + "blackborderdetector" : { "enable" : true, - "threshold" : 0.01 + "threshold" : 0.01, + "unknownFrameCnt": 600, + "borderFrameCnt" : 50, + "maxInconsistentCnt" : 10, + "blurRemoveCnt": 1, + "mode" : "default" }, /// The configuration of the effect engine, contains the following items: diff --git a/include/blackborder/BlackBorderDetector.h b/include/blackborder/BlackBorderDetector.h index d3764ced..a3a1c32b 100644 --- a/include/blackborder/BlackBorderDetector.h +++ b/include/blackborder/BlackBorderDetector.h @@ -49,7 +49,7 @@ namespace hyperion /// Constructs a black-border detector /// @param[in] blackborderThreshold The threshold which the blackborder detector should use /// - BlackBorderDetector(uint8_t blackborderThreshold); + BlackBorderDetector(double threshold); /// /// Performs the actual black-border detection on the given image @@ -58,13 +58,17 @@ namespace hyperion /// /// @return The detected (or not detected) black border info /// + + uint8_t calculateThreshold(double blackborderThreshold); + + /// + /// default detection mode (3lines 4side detection) template BlackBorder process(const Image & image) { - // test center and 33%, 66% of width/heigth // 33 and 66 will check left and top - // center ill check right and bottom sids + // center will check right and bottom sids int width = image.width(); int height = image.height(); int width33percent = width / 3; @@ -79,9 +83,9 @@ namespace hyperion int firstNonBlackYPixelIndex = -1; // find first X pixel of the image - for (int x = 0; x < width; ++x) + for (int x = 0; x < width33percent; ++x) { - const Pixel_T & color1 = image( (width - x), yCenter); // right side center line check + const Pixel_T & color1 = image( (width - 1 - x), yCenter); // right side center line check const Pixel_T & color2 = image(x, height33percent); const Pixel_T & color3 = image(x, height66percent); if (!isBlack(color1) || !isBlack(color2) || !isBlack(color3)) @@ -92,9 +96,9 @@ namespace hyperion } // find first Y pixel of the image - for (int y = 0; y < height; ++y) + for (int y = 0; y < height33percent; ++y) { - const Pixel_T & color1 = image(xCenter, (height - y)); // bottom center line check + const Pixel_T & color1 = image(xCenter, (height - 1 - y)); // bottom center line check const Pixel_T & color2 = image(width33percent, y ); const Pixel_T & color3 = image(width66percent, y); if (!isBlack(color1) || !isBlack(color2) || !isBlack(color3)) @@ -112,6 +116,120 @@ namespace hyperion return detectedBorder; } + + /// + /// classic detection mode (topleft single line mode) + template + BlackBorder process_classic(const Image & image) + { + // only test the topleft third of the image + int width = image.width() /3; + int height = image.height() / 3; + int maxSize = std::max(width, height); + + int firstNonBlackXPixelIndex = -1; + int firstNonBlackYPixelIndex = -1; + + // find some pixel of the image + for (int i = 0; i < maxSize; ++i) + { + int x = std::min(i, width); + int y = std::min(i, height); + + const Pixel_T & color = image(x, y); + if (!isBlack(color)) + { + firstNonBlackXPixelIndex = x; + firstNonBlackYPixelIndex = y; + break; + } + } + + // expand image to the left + for(; firstNonBlackXPixelIndex > 0; --firstNonBlackXPixelIndex) + { + const Pixel_T & color = image(firstNonBlackXPixelIndex-1, firstNonBlackYPixelIndex); + if (isBlack(color)) + { + break; + } + } + + // expand image to the top + for(; firstNonBlackYPixelIndex > 0; --firstNonBlackYPixelIndex) + { + const Pixel_T & color = image(firstNonBlackXPixelIndex, firstNonBlackYPixelIndex-1); + if (isBlack(color)) + { + break; + } + } + + // Construct result + BlackBorder detectedBorder; + detectedBorder.unknown = firstNonBlackXPixelIndex == -1 || firstNonBlackYPixelIndex == -1; + detectedBorder.horizontalSize = firstNonBlackYPixelIndex; + detectedBorder.verticalSize = firstNonBlackXPixelIndex; + return detectedBorder; + } + + +// osd detection mode (find x then y at detected x to avoid changes by osd overlays) + template + BlackBorder process_osd(const Image & image) + { + // find X position at height33 and height66 we check from the left side, Ycenter will check from right side + // then we try to find a pixel at this X position from top and bottom and right side from top + int width = image.width(); + int height = image.height(); + int width33percent = width / 3; + int height33percent = height / 3; +// int width66percent = width33percent * 2; + int height66percent = height33percent * 2; +// int xCenter = width / 2; + int yCenter = height / 2; + + + int firstNonBlackXPixelIndex = -1; + int firstNonBlackYPixelIndex = -1; + + // find first X pixel of the image + int x; + for (x = 0; x < width33percent; ++x) + { + const Pixel_T & color1 = image( (width - 1 - x), yCenter); // right side center line check + const Pixel_T & color2 = image(x, height33percent); + const Pixel_T & color3 = image(x, height66percent); + if (!isBlack(color1) || !isBlack(color2) || !isBlack(color3)) + { + firstNonBlackXPixelIndex = x; + break; + } + } + + // find first Y pixel of the image + for (int y = 0; y < height33percent; ++y) + { + const Pixel_T & color1 = image(x, (height - 1 - y)); // left side bottom check + const Pixel_T & color2 = image(x, y );// left side top check + const Pixel_T & color3 = image( (width - 1 - x), y); // right side top check + if (!isBlack(color1) || !isBlack(color2) || !isBlack(color3)) + { + firstNonBlackYPixelIndex = y; + break; + } + } + + // Construct result + BlackBorder detectedBorder; + detectedBorder.unknown = firstNonBlackXPixelIndex == -1 || firstNonBlackYPixelIndex == -1; + detectedBorder.horizontalSize = firstNonBlackYPixelIndex; + detectedBorder.verticalSize = firstNonBlackXPixelIndex; + return detectedBorder; + } + + + private: /// @@ -131,5 +249,6 @@ namespace hyperion private: /// Threshold for the blackborder detector [0 .. 255] const uint8_t _blackborderThreshold; + }; } // end namespace hyperion diff --git a/include/blackborder/BlackBorderProcessor.h b/include/blackborder/BlackBorderProcessor.h index 14be0585..443b1789 100644 --- a/include/blackborder/BlackBorderProcessor.h +++ b/include/blackborder/BlackBorderProcessor.h @@ -1,6 +1,8 @@ #pragma once +// Jsoncpp includes +#include // Local Hyperion includes #include "BlackBorderDetector.h" @@ -23,11 +25,7 @@ namespace hyperion /// outer pixels is blurred (black and color combined due to image scaling)) /// @param[in] blackborderThreshold The threshold which the blackborder detector should use /// - BlackBorderProcessor( - const unsigned unknownFrameCnt, - const unsigned borderFrameCnt, - const unsigned blurRemoveCnt, - uint8_t blackborderThreshold); + BlackBorderProcessor(const Json::Value &blackborderConfig); /// /// Return the current (detected) border @@ -48,7 +46,14 @@ namespace hyperion bool process(const Image & image) { // get the border for the single image - BlackBorder imageBorder = _detector.process(image); + BlackBorder imageBorder; + if (_detectionMode == "default") { + imageBorder = _detector.process(image); + } else if (_detectionMode == "classic") { + imageBorder = _detector.process_classic(image); + } else if (_detectionMode == "osd") { + imageBorder = _detector.process_osd(image); + } // add blur to the border if (imageBorder.horizontalSize > 0) { @@ -80,9 +85,15 @@ namespace hyperion /// The number of horizontal/vertical borders detected before it becomes the current border const unsigned _borderSwitchCnt; + // The number of frames that are "ignored" before a new border gets set as _previousDetectedBorder + const unsigned _maxInconsistentCnt; + /// The number of pixels to increase a detected border for removing blury pixels unsigned _blurRemoveCnt; + /// The border detection mode + const std::string _detectionMode; + /// The blackborder detector BlackBorderDetector _detector; @@ -96,5 +107,6 @@ namespace hyperion unsigned _consistentCnt; /// The number of frame the previous detected border NOT matched the incomming border unsigned _inconsistentCnt; + }; } // end namespace hyperion diff --git a/include/hyperion/ImageProcessor.h b/include/hyperion/ImageProcessor.h index 5049ac03..338c18e7 100644 --- a/include/hyperion/ImageProcessor.h +++ b/include/hyperion/ImageProcessor.h @@ -106,7 +106,7 @@ private: /// @param[in] enableBlackBorderDetector Flag indicating if the blacborder detector should be enabled /// @param[in] blackborderThreshold The threshold which the blackborder detector should use /// - ImageProcessor(const LedString &ledString, bool enableBlackBorderDetector, uint8_t blackborderThreshold); + ImageProcessor(const LedString &ledString, const Json::Value &blackborderConfig); /// /// Performs black-border detection (if enabled) on the given image diff --git a/include/hyperion/ImageProcessorFactory.h b/include/hyperion/ImageProcessorFactory.h index cde64440..e988e283 100644 --- a/include/hyperion/ImageProcessorFactory.h +++ b/include/hyperion/ImageProcessorFactory.h @@ -33,7 +33,7 @@ public: /// @param[in] enableBlackBorderDetector Flag indicating if the blacborder detector should be enabled /// @param[in] blackborderThreshold The threshold which the blackborder detector should use /// - void init(const LedString& ledString, bool enableBlackBorderDetector, double blackborderThreshold); + void init(const LedString& ledString, const Json::Value &blackborderConfig); /// /// Creates a new ImageProcessor. The onwership of the processor is transferred to the caller. @@ -46,9 +46,6 @@ private: /// The Led-string specification LedString _ledString; - /// Flag indicating if the black border detector should be used - bool _enableBlackBorderDetector; - - /// Threshold for the blackborder detector [0 .. 255] - uint8_t _blackborderThreshold; + // Reference to the blackborder json configuration values + Json::Value _blackborderConfig; }; diff --git a/libsrc/blackborder/BlackBorderDetector.cpp b/libsrc/blackborder/BlackBorderDetector.cpp index 6164f920..60a57c30 100644 --- a/libsrc/blackborder/BlackBorderDetector.cpp +++ b/libsrc/blackborder/BlackBorderDetector.cpp @@ -1,11 +1,26 @@ - +#include // BlackBorders includes #include using namespace hyperion; -BlackBorderDetector::BlackBorderDetector(uint8_t blackborderThreshold) : - _blackborderThreshold(blackborderThreshold) +BlackBorderDetector::BlackBorderDetector(double threshold) : + _blackborderThreshold(calculateThreshold(threshold)) { // empty } + +uint8_t BlackBorderDetector::calculateThreshold(double threshold) +{ + int rgbThreshold = int(std::ceil(threshold * 255)); + if (rgbThreshold < 0) + rgbThreshold = 0; + else if (rgbThreshold > 255) + rgbThreshold = 255; + + uint8_t blackborderThreshold = uint8_t(rgbThreshold); + + std::cout << "Black border threshold set to " << threshold << " (" << int(blackborderThreshold) << ")" << std::endl; + + return blackborderThreshold; +} diff --git a/libsrc/blackborder/BlackBorderProcessor.cpp b/libsrc/blackborder/BlackBorderProcessor.cpp index 78e718fe..d6678d51 100644 --- a/libsrc/blackborder/BlackBorderProcessor.cpp +++ b/libsrc/blackborder/BlackBorderProcessor.cpp @@ -1,23 +1,31 @@ -//#include - +//* +#include +/* +#include +using std::cout; +using std::endl; +using std::setw; +using std::left; +//*/ // Blackborder includes #include + using namespace hyperion; -BlackBorderProcessor::BlackBorderProcessor(const unsigned unknownFrameCnt, - const unsigned borderFrameCnt, - const unsigned blurRemoveCnt, - uint8_t blackborderThreshold) : - _unknownSwitchCnt(unknownFrameCnt), - _borderSwitchCnt(borderFrameCnt), - _blurRemoveCnt(blurRemoveCnt), - _detector(blackborderThreshold), +BlackBorderProcessor::BlackBorderProcessor(const Json::Value &blackborderConfig) : + _unknownSwitchCnt(blackborderConfig.get("unknownFrameCnt", 600).asUInt()), + _borderSwitchCnt(blackborderConfig.get("borderFrameCnt", 50).asUInt()), + _maxInconsistentCnt(blackborderConfig.get("maxInconsistentCnt", 10).asUInt()), + _blurRemoveCnt(blackborderConfig.get("blurRemoveCnt", 1).asUInt()), + _detectionMode(blackborderConfig.get("mode", "default").asString()), + _detector(blackborderConfig.get("threshold", 0.01).asDouble()), _currentBorder({true, -1, -1}), _previousDetectedBorder({true, -1, -1}), _consistentCnt(0), _inconsistentCnt(10) { + std::cout << "DETECTION MODE:" << _detectionMode << std::endl; // empty } @@ -39,7 +47,7 @@ bool BlackBorderProcessor::updateBorder(const BlackBorder & newDetectedBorder) // makes it look like the border detectionn is not working - since the new 3 line detection algorithm is more precise this became a problem specialy in dark scenes // wisc -// std::cout << "cur: " << _currentBorder.verticalSize << " " << _currentBorder.horizontalSize << " new: " << newDetectedBorder.verticalSize << " " << newDetectedBorder.horizontalSize << " c:i " << _consistentCnt << ":" << _inconsistentCnt << std::endl; +// std::cout << "c: " << setw(2) << _currentBorder.verticalSize << " " << setw(2) << _currentBorder.horizontalSize << " p: " << setw(2) << _previousDetectedBorder.verticalSize << " " << setw(2) << _previousDetectedBorder.horizontalSize << " n: " << setw(2) << newDetectedBorder.verticalSize << " " << setw(2) << newDetectedBorder.horizontalSize << " c:i " << setw(2) << _consistentCnt << ":" << setw(2) << _inconsistentCnt << std::endl; // set the consistency counter if (newDetectedBorder == _previousDetectedBorder) @@ -50,7 +58,7 @@ bool BlackBorderProcessor::updateBorder(const BlackBorder & newDetectedBorder) else { ++_inconsistentCnt; - if (_inconsistentCnt <= 10)// few inconsistent frames + if (_inconsistentCnt <= _maxInconsistentCnt)// only few inconsistent frames { //discard the newDetectedBorder -> keep the consistent count for previousDetectedBorder return false; diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index d6657046..37c77927 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -282,8 +282,8 @@ Hyperion::Hyperion(const Json::Value &jsonConfig) : // initialize the image processor factory ImageProcessorFactory::getInstance().init( _ledString, - jsonConfig["blackborderdetector"].get("enable", true).asBool(), - jsonConfig["blackborderdetector"].get("threshold", 0.01).asDouble()); + jsonConfig["blackborderdetector"] + ); // initialize the color smoothing filter _device = createColorSmoothing(jsonConfig["color"]["smoothing"], _device); diff --git a/libsrc/hyperion/ImageProcessor.cpp b/libsrc/hyperion/ImageProcessor.cpp index 25784d0a..f83dcc27 100644 --- a/libsrc/hyperion/ImageProcessor.cpp +++ b/libsrc/hyperion/ImageProcessor.cpp @@ -8,10 +8,11 @@ using namespace hyperion; -ImageProcessor::ImageProcessor(const LedString& ledString, bool enableBlackBorderDetector, uint8_t blackborderThreshold) : +//ImageProcessor::ImageProcessor(const LedString& ledString, bool enableBlackBorderDetector, uint8_t blackborderThreshold) : +ImageProcessor::ImageProcessor(const LedString& ledString, const Json::Value & blackborderConfig) : _ledString(ledString), - _enableBlackBorderRemoval(enableBlackBorderDetector), - _borderProcessor(new BlackBorderProcessor(600, 50, 1, blackborderThreshold)), + _enableBlackBorderRemoval(blackborderConfig.get("enable", true).asBool()), + _borderProcessor(new BlackBorderProcessor(blackborderConfig) ), _imageToLeds(nullptr) { // empty diff --git a/libsrc/hyperion/ImageProcessorFactory.cpp b/libsrc/hyperion/ImageProcessorFactory.cpp index a47e532f..c85b35cd 100644 --- a/libsrc/hyperion/ImageProcessorFactory.cpp +++ b/libsrc/hyperion/ImageProcessorFactory.cpp @@ -13,25 +13,13 @@ ImageProcessorFactory& ImageProcessorFactory::getInstance() return instance; } -void ImageProcessorFactory::init(const LedString& ledString, bool enableBlackBorderDetector, double blackborderThreshold) +void ImageProcessorFactory::init(const LedString& ledString, const Json::Value & blackborderConfig) { _ledString = ledString; - _enableBlackBorderDetector = enableBlackBorderDetector; - - int threshold = int(std::ceil(blackborderThreshold * 255)); - if (threshold < 0) - threshold = 0; - else if (threshold > 255) - threshold = 255; - _blackborderThreshold = uint8_t(threshold); - - if (_enableBlackBorderDetector) - { - std::cout << "Black border threshold set to " << blackborderThreshold << " (" << int(_blackborderThreshold) << ")" << std::endl; - } + _blackborderConfig = blackborderConfig; } ImageProcessor* ImageProcessorFactory::newImageProcessor() const { - return new ImageProcessor(_ledString, _enableBlackBorderDetector, _blackborderThreshold); + return new ImageProcessor(_ledString, _blackborderConfig); } diff --git a/test/TestBlackBorderProcessor.cpp b/test/TestBlackBorderProcessor.cpp index e05c630f..88879398 100644 --- a/test/TestBlackBorderProcessor.cpp +++ b/test/TestBlackBorderProcessor.cpp @@ -44,11 +44,13 @@ Image createImage(unsigned width, unsigned height, unsigned topBorder, int main() { - unsigned unknownCnt = 600; +// unsigned unknownCnt = 600; unsigned borderCnt = 50; - unsigned blurCnt = 0; +// unsigned blurCnt = 0; + Json::Value config; - BlackBorderProcessor processor(unknownCnt, borderCnt, blurCnt, 3); +// BlackBorderProcessor processor(unknownCnt, borderCnt, blurCnt, 3, config); + BlackBorderProcessor processor(config); // Start with 'no border' detection Image noBorderImage = createImage(64, 64, 0, 0);