From 2eea311dcef21d6dedc3f1712238b7d1b69c89be Mon Sep 17 00:00:00 2001 From: pcaffardi Date: Sun, 9 Aug 2015 16:15:25 +0200 Subject: [PATCH 1/9] Update LedDeviceAPA102.cpp This fix the previous limit of 64 APA102 leds, because of too short end frame. Now the end frame is computed accordling to this documentation: https://cpldcpu.wordpress.com/2014/11/30/understanding-the-apa102-superled/. Tested on my 98 leds, it works fine. I suggest to modify hyperion to allow LED drivers to apply the brightness parameter because APA102 has a parameter for that, without the need to elaborate RGB color to simulate it (result is wrong colors!). Is it possible to introduce such parameter in LED drivers and let the driver apply that? Former-commit-id: 2d714e6eb075ec57e0973839fe96d2d7a051c57f --- libsrc/leddevice/LedDeviceAPA102.cpp | 48 ++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/libsrc/leddevice/LedDeviceAPA102.cpp b/libsrc/leddevice/LedDeviceAPA102.cpp index 2af01acf..00eba4bd 100644 --- a/libsrc/leddevice/LedDeviceAPA102.cpp +++ b/libsrc/leddevice/LedDeviceAPA102.cpp @@ -1,4 +1,3 @@ - // STL includes #include #include @@ -18,22 +17,45 @@ LedDeviceAPA102::LedDeviceAPA102(const std::string& outputDevice, const unsigned // empty } +#define MIN(a,b) ((a)<(b)?(a):(b)) +#define MAX(a,b) ((a)>(b)?(a):(b)) + +#define APA102_START_FRAME_BYTES 4 +#define APA102_LED_BYTES 4 +#define APA102_END_FRAME_BITS_MIN 32 +#define APA102_END_FRAME_BITS(leds) MAX((((leds-1)/2)+1),APA102_END_FRAME_BITS_MIN) +#define APA102_END_FRAME_BYTES(leds) (((APA102_END_FRAME_BITS(leds)-1)/8)+1) +#define APA102_LED_HEADER 0xe0 +#define APA102_LED_MAX_INTENSITY 0x1f + int LedDeviceAPA102::write(const std::vector &ledValues) { - const unsigned int startFrameSize = 4; - const unsigned int endFrameSize = (ledValues.size() + 63) / 64 * 4; - const unsigned int mLedCount = (ledValues.size() * 4) + startFrameSize + endFrameSize; - if(_ledBuffer.size() != mLedCount){ - _ledBuffer.resize(mLedCount, 0x00); + const unsigned int startFrameSize = APA102_START_FRAME_BYTES; + const unsigned int ledsCount = ledValues.size() ; + const unsigned int ledsSize = ledsCount * APA102_LED_BYTES ; + const unsigned int endFrameBits = APA102_END_FRAME_BITS(ledsCount) ; + const unsigned int endFrameSize = APA102_END_FRAME_BYTES(ledsCount) ; + const unsigned int transferSize = startFrameSize + ledsSize + endFrameSize ; + + if(_ledBuffer.size() != transferSize){ + _ledBuffer.resize(transferSize, 0x00); + } + + unsigned idx = 0, i; + for (i=0; i &ledValues) int LedDeviceAPA102::switchOff() { return write(std::vector(_ledBuffer.size(), ColorRgb{0,0,0})); -} +} From bbfded147e216bc6fe147ef82aef5bc9a02fe10d Mon Sep 17 00:00:00 2001 From: frostworx Date: Fri, 22 Jan 2016 17:17:45 +0100 Subject: [PATCH 2/9] minor qt5 fix Former-commit-id: 65ab3bb14d6a7ab52e6ab1e8eaa85321dc5fa543 --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index 21d397a6..ee019e18 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -218,7 +218,7 @@ void LedDevicePhilipsHue::put(QString route, QString content) { QString url = QString("http://%1/api/%2/%3").arg(host).arg(username).arg(route); // Perfrom request QNetworkRequest request(url); - QNetworkReply* reply = manager->put(request, content.toAscii()); + QNetworkReply* reply = manager->put(request, content.toLatin1()); // Connect finished signal to quit slot of the loop. QEventLoop loop; loop.connect(reply, SIGNAL(finished()), SLOT(quit())); From a257f185c93ff304e6ed1b358ed3266bf211c971 Mon Sep 17 00:00:00 2001 From: redpanther Date: Tue, 26 Jan 2016 15:08:21 +0100 Subject: [PATCH 3/9] increase fadecandy maximum amount of leds remove debug code Former-commit-id: d74025128978e4e26d49fb57215f660bd824895b --- libsrc/leddevice/LedDeviceFadeCandy.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/libsrc/leddevice/LedDeviceFadeCandy.cpp b/libsrc/leddevice/LedDeviceFadeCandy.cpp index e8c178d1..a9548842 100755 --- a/libsrc/leddevice/LedDeviceFadeCandy.cpp +++ b/libsrc/leddevice/LedDeviceFadeCandy.cpp @@ -1,6 +1,6 @@ #include "LedDeviceFadeCandy.h" -static const unsigned MAX_NUM_LEDS = 512; +static const unsigned MAX_NUM_LEDS = 10000; static const unsigned OPC_BROADCAST = 0; // OPC broadcast channel static const unsigned OPC_SET_PIXELS = 0; // OPC command codes static const unsigned OPC_HEADER_SIZE = 4; // OPC header size @@ -32,11 +32,8 @@ bool LedDeviceFadeCandy::isConnected() bool LedDeviceFadeCandy::tryConnect() { if ( _client.state() == QAbstractSocket::UnconnectedState ) { - qDebug("connecting to %s %i",_host.c_str(),_port); - _client.connectToHost( _host.c_str(), _port); - if ( _client.waitForConnected(1000) ) - qDebug("connected"); + _client.waitForConnected(1000); } return isConnected(); From b6060d392efb289eec74ac93e6eeaec422920c29 Mon Sep 17 00:00:00 2001 From: redpanther Date: Wed, 27 Jan 2016 10:44:51 +0100 Subject: [PATCH 4/9] make opc channel available in via config. Former-commit-id: 6a065cd049e29d7184a3aa1454de0fe1855e9941 --- libsrc/leddevice/LedDeviceFactory.cpp | 7 ++++--- libsrc/leddevice/LedDeviceFadeCandy.cpp | 18 +++++++++--------- libsrc/leddevice/LedDeviceFadeCandy.h | 3 ++- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index b0fd064f..f692b03c 100755 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -246,9 +246,10 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) } else if (type == "fadecandy") { - const std::string host = deviceConfig.get("output", "127.0.0.1").asString(); - const uint16_t port = deviceConfig.get("port", 7890).asInt(); - device = new LedDeviceFadeCandy(host,port); + const std::string host = deviceConfig.get("output", "127.0.0.1").asString(); + const uint16_t port = deviceConfig.get("port", 7890).asInt(); + const uint16_t channel = deviceConfig.get("channel", 0).asInt(); + device = new LedDeviceFadeCandy(host, port, channel); } else if (type == "tpm2") { diff --git a/libsrc/leddevice/LedDeviceFadeCandy.cpp b/libsrc/leddevice/LedDeviceFadeCandy.cpp index a9548842..685795a9 100755 --- a/libsrc/leddevice/LedDeviceFadeCandy.cpp +++ b/libsrc/leddevice/LedDeviceFadeCandy.cpp @@ -1,16 +1,15 @@ #include "LedDeviceFadeCandy.h" -static const unsigned MAX_NUM_LEDS = 10000; -static const unsigned OPC_BROADCAST = 0; // OPC broadcast channel -static const unsigned OPC_SET_PIXELS = 0; // OPC command codes -static const unsigned OPC_HEADER_SIZE = 4; // OPC header size +static const unsigned MAX_NUM_LEDS = 10000; // OPC can handle 21845 leds - in theory, fadecandy device should handle 10000 leds +static const unsigned OPC_SET_PIXELS = 0; // OPC command codes +static const unsigned OPC_HEADER_SIZE = 4; // OPC header size -LedDeviceFadeCandy::LedDeviceFadeCandy(const std::string& host, const uint16_t port) : - _host(host), _port(port) +LedDeviceFadeCandy::LedDeviceFadeCandy(const std::string& host, const uint16_t port, const unsigned channel) : + _host(host), _port(port), _channel(channel) { _opc_data.resize( OPC_HEADER_SIZE ); - _opc_data[0] = OPC_BROADCAST; + _opc_data[0] = channel; _opc_data[1] = OPC_SET_PIXELS; _opc_data[2] = 0; _opc_data[3] = 0; @@ -33,7 +32,8 @@ bool LedDeviceFadeCandy::tryConnect() { if ( _client.state() == QAbstractSocket::UnconnectedState ) { _client.connectToHost( _host.c_str(), _port); - _client.waitForConnected(1000); + if ( _client.waitForConnected(1000) ) + qDebug("fadecandy/opc: connected to %s:%i on channel %i", _host.c_str(), _port, _channel); } return isConnected(); @@ -48,7 +48,7 @@ int LedDeviceFadeCandy::write( const std::vector & ledValues ) if (nrLedValues > MAX_NUM_LEDS) { - std::cerr << "Invalid attempt to write led values. Not more than " << MAX_NUM_LEDS << " leds are allowed." << std::endl; + std::cerr << "fadecandy/opc: Invalid attempt to write led values. Not more than " << MAX_NUM_LEDS << " leds are allowed." << std::endl; return -1; } diff --git a/libsrc/leddevice/LedDeviceFadeCandy.h b/libsrc/leddevice/LedDeviceFadeCandy.h index 0b894e88..ebcd85d9 100755 --- a/libsrc/leddevice/LedDeviceFadeCandy.h +++ b/libsrc/leddevice/LedDeviceFadeCandy.h @@ -23,7 +23,7 @@ public: /// @param host The ip address/host name of fadecandy/opc server /// @param port The port to use (fadecandy default is 7890) /// - LedDeviceFadeCandy(const std::string& host, const uint16_t port); + LedDeviceFadeCandy(const std::string& host, const uint16_t port, const unsigned channel); /// /// Destructor of the LedDevice; closes the tcp client @@ -46,6 +46,7 @@ private: QTcpSocket _client; const std::string _host; const uint16_t _port; + const unsigned _channel; QByteArray _opc_data; /// try to establish connection to opc server, if not connected yet From 8a906b9d01c247895ceb60f2206b8136b7efa5b5 Mon Sep 17 00:00:00 2001 From: redpanther Date: Sun, 31 Jan 2016 04:34:13 +0100 Subject: [PATCH 5/9] make priority of boot effect adjustable. It is set to 0 to not alter to current behaciour. A value of 900 could be more feasable, because boot effect with prio 0 isn't overwritable by other effects Former-commit-id: 1852339e6ade62c86719cfbb47faa46eb8f8d4ed --- config/hyperion.config.json | 6 ++++-- config/hyperion_x86.config.json | 6 ++++-- src/hyperiond/hyperiond.cpp | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/config/hyperion.config.json b/config/hyperion.config.json index 44931ac4..b16b845e 100644 --- a/config/hyperion.config.json +++ b/config/hyperion.config.json @@ -364,6 +364,7 @@ /// The configuration of the effect engine, contains the following items: /// * paths : An array with absolute location(s) of directories with effects /// * bootsequence : The effect selected as 'boot sequence' + /// * duration_ms : duration of boot effect in ms. 0 means effect stays forever "effects" : { "paths" : @@ -374,8 +375,9 @@ "bootsequence" : { - "effect" : "Rainbow swirl fast", - "duration_ms" : 3000 + "effect" : "Rainbow swirl fast", + "duration_ms" : 3000, + "priority" : 0 }, /// The configuration for the frame-grabber, contains the following items: diff --git a/config/hyperion_x86.config.json b/config/hyperion_x86.config.json index 4abd64db..06e35136 100644 --- a/config/hyperion_x86.config.json +++ b/config/hyperion_x86.config.json @@ -359,6 +359,7 @@ /// The configuration of the effect engine, contains the following items: /// * paths : An array with absolute location(s) of directories with effects /// * bootsequence : The effect selected as 'boot sequence' + /// * duration_ms : duration of boot effect in ms. 0 means effect stays forever "effects" : { "paths" : @@ -369,8 +370,9 @@ "bootsequence" : { - "effect" : "Rainbow swirl fast", - "duration_ms" : 3000 + "effect" : "Rainbow swirl fast", + "duration_ms" : 3000, + "priority" : 0 }, /// The configuration for the frame-grabber, contains the following items: diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index 7357d55f..b6289fe2 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -126,7 +126,7 @@ int main(int argc, char** argv) // Get the parameters for the bootsequence const std::string effectName = effectConfig["effect"].asString(); const unsigned duration_ms = effectConfig["duration_ms"].asUInt(); - const int priority = 0; + const int priority = effectConfig["priority"].asUInt(); hyperion.setColor(priority+1, ColorRgb::BLACK, duration_ms, false); From 4decb05348fa80c65c26f8f857f57f860d2964ac Mon Sep 17 00:00:00 2001 From: redpanther Date: Sun, 31 Jan 2016 22:38:30 +0100 Subject: [PATCH 6/9] adds ability to set static color on boot. Former-commit-id: 41ca5ba73fde698d73380fac3dcee5e33b310d76 --- config/hyperion.config.json | 4 ++++ config/hyperion_x86.config.json | 6 +++++- src/hyperiond/hyperiond.cpp | 20 +++++++++++++++++--- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/config/hyperion.config.json b/config/hyperion.config.json index b16b845e..34f485cf 100644 --- a/config/hyperion.config.json +++ b/config/hyperion.config.json @@ -364,7 +364,10 @@ /// The configuration of the effect engine, contains the following items: /// * paths : An array with absolute location(s) of directories with effects /// * bootsequence : The effect selected as 'boot sequence' + /// * effect : name of the effect you want to start. Set to empty if no effect wanted + /// * color : switch to static color after effect is done /// * duration_ms : duration of boot effect in ms. 0 means effect stays forever + /// * priority : priority of boot effect and static color "effects" : { "paths" : @@ -375,6 +378,7 @@ "bootsequence" : { + "color" : [0,0,0], "effect" : "Rainbow swirl fast", "duration_ms" : 3000, "priority" : 0 diff --git a/config/hyperion_x86.config.json b/config/hyperion_x86.config.json index 06e35136..4121c499 100644 --- a/config/hyperion_x86.config.json +++ b/config/hyperion_x86.config.json @@ -359,7 +359,10 @@ /// The configuration of the effect engine, contains the following items: /// * paths : An array with absolute location(s) of directories with effects /// * bootsequence : The effect selected as 'boot sequence' + /// * effect : name of the effect you want to start. Set to empty if no effect wanted + /// * color : switch to static color after effect is done /// * duration_ms : duration of boot effect in ms. 0 means effect stays forever + /// * priority : priority of boot effect and static color "effects" : { "paths" : @@ -370,9 +373,10 @@ "bootsequence" : { + "color" : [0,0,0], "effect" : "Rainbow swirl fast", "duration_ms" : 3000, - "priority" : 0 + "priority" : 900 }, /// The configuration for the frame-grabber, contains the following items: diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index b6289fe2..032d44db 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -127,19 +127,33 @@ int main(int argc, char** argv) const std::string effectName = effectConfig["effect"].asString(); const unsigned duration_ms = effectConfig["duration_ms"].asUInt(); const int priority = effectConfig["priority"].asUInt(); + const int bootcolor_priority = (priority > 990) ? priority+1 : 990; - hyperion.setColor(priority+1, ColorRgb::BLACK, duration_ms, false); + if ( ! effectConfig["color"].isNull() && effectConfig["color"].isArray() && effectConfig["color"].size() == 3 ) + { + ColorRgb boot_color = { + (uint8_t)effectConfig["color"][0].asUInt(), + (uint8_t)effectConfig["color"][1].asUInt(), + (uint8_t)effectConfig["color"][2].asUInt() + }; + + hyperion.setColor(bootcolor_priority, boot_color, 0, false); + } + else + { + hyperion.setColor(bootcolor_priority, ColorRgb::BLACK, duration_ms, false); + } if (effectConfig.isMember("args")) { const Json::Value effectConfigArgs = effectConfig["args"]; if (hyperion.setEffect(effectName, effectConfigArgs, priority, duration_ms) == 0) { - std::cout << "Boot sequence(" << effectName << ") with user-defined arguments created and started" << std::endl; + std::cout << "Boot sequence(" << effectName << ") with user-defined arguments created and started" << std::endl; } else { - std::cout << "Failed to start boot sequence: " << effectName << " with user-defined arguments" << std::endl; + std::cout << "Failed to start boot sequence: " << effectName << " with user-defined arguments" << std::endl; } } else From e8a441ca987b9049bf115c78a4c477a02f738451 Mon Sep 17 00:00:00 2001 From: redpanther Date: Thu, 4 Feb 2016 13:17:40 +0100 Subject: [PATCH 7/9] add fadecandy/opc specs Former-commit-id: 287fa1501e569df439eed8aac6dd44077ecb0994 --- doc/datasheets/fadecandy_opc_protocol.md | 79 ++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 doc/datasheets/fadecandy_opc_protocol.md diff --git a/doc/datasheets/fadecandy_opc_protocol.md b/doc/datasheets/fadecandy_opc_protocol.md new file mode 100644 index 00000000..53cec6a5 --- /dev/null +++ b/doc/datasheets/fadecandy_opc_protocol.md @@ -0,0 +1,79 @@ +Fadecandy: Open Pixel Control Protocol +====================================== + +The Fadecandy Server (`fcserver`) operates as a bridge between LED controllers attached over USB, and visual effects that communicate via a TCP socket. + +The primary protocol supported by `fcserver` is [Open Pixel Control](http://openpixelcontrol.org), a super simple way to send RGB values over a socket. We support the standard Open Pixel Control commands, as well as some Fadecandy extensions. + +Socket +------ + +Open Pixel Control uses a TCP socket, by default on port 7890. For the best performance, remember to set TCP_NODELAY socket option. + +Command Format +-------------- + +All OPC commands follow the same general format. All multi-byte values in Open Pixel Control are in network byte order, high byte followed by low byte. + +Channel | Command | Length (N) | Data +---------- | --------- | ---------- | -------------------------- +1 byte | 1 byte | 2 bytes | N bytes of message data + +Set Pixel Colors +---------------- + +Video data arrives in a **Set Pixel Colors** command: + +Byte | **Set Pixel Colors** command +------ | -------------------------------- +0 | Channel Number +1 | Command (0x00) +2 - 3 | Data length +4 | Pixel #0, Red +5 | Pixel #0, Green +6 | Pixel #0, Blue +7 | Pixel #1, Red +8 | Pixel #1, Green +9 | Pixel #1, Blue +… | … + +As soon as a complete Set Pixel Colors command is received, a new frame of video will be broadcast simultaneously to all attached Fadecandy devices. + +Set Global Color Correction +--------------------------- + +The color correction data (from the 'color' configuration key) can also be changed at runtime, by sending a new blob of JSON text in a Fadecandy-specific command. Fadecandy's 16-bit System ID for Open Pixel Control's System Exclusive (0xFF) command is **0x0001**. + +Byte | **Set Global Color Correction** command +------ | ------------------------------------------ +0 | Channel Number (0x00, reserved) +1 | Command (0xFF, System Exclusive) +2 - 3 | Data length (JSON Length + 4) +4 - 5 | System ID (0x0001, Fadecandy) +6 - 7 | SysEx ID (0x0001, Set Global Color Correction) +8 - … | JSON Text + +Set Firmware Configuration +-------------------------- + +The firmware supports some runtime configuration options. Any OPC client can send a new firmware configuration packet using this command. If the supplied data is shorter than the firmware's configuration buffer, only the provided bytes will be changed. + +Byte | **Set Firmware Configuration** command +------ | ------------------------------------------ +0 | Channel Number (0x00, reserved) +1 | Command (0xFF, System Exclusive) +2 - 3 | Data length (Configuration Length + 4) +4 - 5 | System ID (0x0001, Fadecandy) +6 - 7 | SysEx ID (0x0002, Set Firmware Configuration) +8 - … | Configuration Data + +Current firmwares support the following configuration options: + +Byte Offset | Bits | Description +----------- | ------ | ------------ +0 | 7 … 4 | (reserved) +0 | 3 | Manual LED control bit +0 | 2 | 0 = LED shows USB activity, 1 = LED under manual control +0 | 1 | Disable keyframe interpolation +0 | 0 | Disable dithering +1 … 62 | 7 … 0 | (reserved) From 2b703de669d838743250d422293756caea51c518 Mon Sep 17 00:00:00 2001 From: wisc Date: Sun, 7 Feb 2016 13:26:40 +0100 Subject: [PATCH 8/9] bugfix, config enhancement and 3 detection modes Former-commit-id: edfc3e7ccf7b7d727e73a8563acb521045026d5b --- config/hyperion.config.json | 15 ++- include/blackborder/BlackBorderDetector.h | 133 ++++++++++++++++++-- include/blackborder/BlackBorderProcessor.h | 24 +++- include/hyperion/ImageProcessor.h | 2 +- include/hyperion/ImageProcessorFactory.h | 9 +- libsrc/blackborder/BlackBorderDetector.cpp | 21 +++- libsrc/blackborder/BlackBorderProcessor.cpp | 32 +++-- libsrc/hyperion/Hyperion.cpp | 4 +- libsrc/hyperion/ImageProcessor.cpp | 7 +- libsrc/hyperion/ImageProcessorFactory.cpp | 18 +-- test/TestBlackBorderProcessor.cpp | 8 +- 11 files changed, 213 insertions(+), 60 deletions(-) diff --git a/config/hyperion.config.json b/config/hyperion.config.json index 44931ac4..9827ba44 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); From 79dda5e8402219c9e78c9711bb59e1a29b428944 Mon Sep 17 00:00:00 2001 From: wisc Date: Sun, 7 Feb 2016 17:40:24 +0100 Subject: [PATCH 9/9] Update LedDeviceAPA102.cpp by pcaffardi Former-commit-id: e7efe8917fc10ceb860e44bbb37442cffa3512db --- libsrc/leddevice/LedDeviceAPA102.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/libsrc/leddevice/LedDeviceAPA102.cpp b/libsrc/leddevice/LedDeviceAPA102.cpp index fb016229..17bec4fc 100644 --- a/libsrc/leddevice/LedDeviceAPA102.cpp +++ b/libsrc/leddevice/LedDeviceAPA102.cpp @@ -2,7 +2,6 @@ #include #include #include -#include // Linux includes #include @@ -32,15 +31,15 @@ LedDeviceAPA102::LedDeviceAPA102(const std::string& outputDevice, const unsigned int LedDeviceAPA102::write(const std::vector &ledValues) { const unsigned int startFrameSize = APA102_START_FRAME_BYTES; - const unsigned int ledsCount = ledValues.size() ; - const unsigned int ledsSize = ledsCount * APA102_LED_BYTES ; - const unsigned int endFrameBits = APA102_END_FRAME_BITS(ledsCount) ; - const unsigned int endFrameSize = APA102_END_FRAME_BYTES(ledsCount) ; - const unsigned int transferSize = startFrameSize + ledsSize + endFrameSize ; + const unsigned int ledsCount = ledValues.size() ; + const unsigned int ledsSize = ledsCount * APA102_LED_BYTES ; + const unsigned int endFrameBits = APA102_END_FRAME_BITS(ledsCount) ; + const unsigned int endFrameSize = APA102_END_FRAME_BYTES(ledsCount) ; + const unsigned int transferSize = startFrameSize + ledsSize + endFrameSize ; if(_ledBuffer.size() != transferSize){ _ledBuffer.resize(transferSize, 0x00); - } + } unsigned idx = 0, i; for (i=0; i &ledValues) _ledBuffer[idx++] = rgb.blue; } for(i=0; i &ledValues) int LedDeviceAPA102::switchOff() { return write(std::vector(_ledBuffer.size(), ColorRgb{0,0,0})); -} +}