Merge remote-tracking branch 'upstream/master'

Former-commit-id: 9b4da8b8c5f61f9507b4775d0f28e187a0246605
This commit is contained in:
redpanther 2016-02-08 15:38:59 +01:00
commit aaba90209f
19 changed files with 376 additions and 102 deletions

View File

@ -355,15 +355,30 @@
/// The black border configuration, contains the following items: /// The black border configuration, contains the following items:
/// * enable : true if the detector should be activated /// * 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) /// * 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, "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: /// The configuration of the effect engine, contains the following items:
/// * paths : An array with absolute location(s) of directories with effects /// * paths : An array with absolute location(s) of directories with effects
/// * bootsequence : The effect selected as 'boot sequence' /// * 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" : "effects" :
{ {
"paths" : "paths" :
@ -374,8 +389,10 @@
"bootsequence" : "bootsequence" :
{ {
"effect" : "Rainbow swirl fast", "color" : [0,0,0],
"duration_ms" : 3000 "effect" : "Rainbow swirl fast",
"duration_ms" : 3000,
"priority" : 0
}, },
/// The configuration for the frame-grabber, contains the following items: /// The configuration for the frame-grabber, contains the following items:

View File

@ -359,6 +359,10 @@
/// The configuration of the effect engine, contains the following items: /// The configuration of the effect engine, contains the following items:
/// * paths : An array with absolute location(s) of directories with effects /// * paths : An array with absolute location(s) of directories with effects
/// * bootsequence : The effect selected as 'boot sequence' /// * 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" : "effects" :
{ {
"paths" : "paths" :
@ -369,8 +373,10 @@
"bootsequence" : "bootsequence" :
{ {
"effect" : "Rainbow swirl fast", "color" : [0,0,0],
"duration_ms" : 3000 "effect" : "Rainbow swirl fast",
"duration_ms" : 3000,
"priority" : 900
}, },
/// The configuration for the frame-grabber, contains the following items: /// The configuration for the frame-grabber, contains the following items:

View File

@ -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)

View File

@ -49,7 +49,7 @@ namespace hyperion
/// Constructs a black-border detector /// Constructs a black-border detector
/// @param[in] blackborderThreshold The threshold which the blackborder detector should use /// @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 /// 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 /// @return The detected (or not detected) black border info
/// ///
uint8_t calculateThreshold(double blackborderThreshold);
///
/// default detection mode (3lines 4side detection)
template <typename Pixel_T> template <typename Pixel_T>
BlackBorder process(const Image<Pixel_T> & image) BlackBorder process(const Image<Pixel_T> & image)
{ {
// test center and 33%, 66% of width/heigth // test center and 33%, 66% of width/heigth
// 33 and 66 will check left and top // 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 width = image.width();
int height = image.height(); int height = image.height();
int width33percent = width / 3; int width33percent = width / 3;
@ -79,9 +83,9 @@ namespace hyperion
int firstNonBlackYPixelIndex = -1; int firstNonBlackYPixelIndex = -1;
// find first X pixel of the image // 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 & color2 = image(x, height33percent);
const Pixel_T & color3 = image(x, height66percent); const Pixel_T & color3 = image(x, height66percent);
if (!isBlack(color1) || !isBlack(color2) || !isBlack(color3)) if (!isBlack(color1) || !isBlack(color2) || !isBlack(color3))
@ -92,9 +96,9 @@ namespace hyperion
} }
// find first Y pixel of the image // 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 & color2 = image(width33percent, y );
const Pixel_T & color3 = image(width66percent, y); const Pixel_T & color3 = image(width66percent, y);
if (!isBlack(color1) || !isBlack(color2) || !isBlack(color3)) if (!isBlack(color1) || !isBlack(color2) || !isBlack(color3))
@ -112,6 +116,120 @@ namespace hyperion
return detectedBorder; return detectedBorder;
} }
///
/// classic detection mode (topleft single line mode)
template <typename Pixel_T>
BlackBorder process_classic(const Image<Pixel_T> & 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 <typename Pixel_T>
BlackBorder process_osd(const Image<Pixel_T> & 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: private:
/// ///
@ -131,5 +249,6 @@ namespace hyperion
private: private:
/// Threshold for the blackborder detector [0 .. 255] /// Threshold for the blackborder detector [0 .. 255]
const uint8_t _blackborderThreshold; const uint8_t _blackborderThreshold;
}; };
} // end namespace hyperion } // end namespace hyperion

View File

@ -1,6 +1,8 @@
#pragma once #pragma once
// Jsoncpp includes
#include <json/json.h>
// Local Hyperion includes // Local Hyperion includes
#include "BlackBorderDetector.h" #include "BlackBorderDetector.h"
@ -23,11 +25,7 @@ namespace hyperion
/// outer pixels is blurred (black and color combined due to image scaling)) /// outer pixels is blurred (black and color combined due to image scaling))
/// @param[in] blackborderThreshold The threshold which the blackborder detector should use /// @param[in] blackborderThreshold The threshold which the blackborder detector should use
/// ///
BlackBorderProcessor( BlackBorderProcessor(const Json::Value &blackborderConfig);
const unsigned unknownFrameCnt,
const unsigned borderFrameCnt,
const unsigned blurRemoveCnt,
uint8_t blackborderThreshold);
/// ///
/// Return the current (detected) border /// Return the current (detected) border
@ -48,7 +46,14 @@ namespace hyperion
bool process(const Image<Pixel_T> & image) bool process(const Image<Pixel_T> & image)
{ {
// get the border for the single 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 // add blur to the border
if (imageBorder.horizontalSize > 0) if (imageBorder.horizontalSize > 0)
{ {
@ -80,9 +85,15 @@ namespace hyperion
/// The number of horizontal/vertical borders detected before it becomes the current border /// The number of horizontal/vertical borders detected before it becomes the current border
const unsigned _borderSwitchCnt; 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 /// The number of pixels to increase a detected border for removing blury pixels
unsigned _blurRemoveCnt; unsigned _blurRemoveCnt;
/// The border detection mode
const std::string _detectionMode;
/// The blackborder detector /// The blackborder detector
BlackBorderDetector _detector; BlackBorderDetector _detector;
@ -96,5 +107,6 @@ namespace hyperion
unsigned _consistentCnt; unsigned _consistentCnt;
/// The number of frame the previous detected border NOT matched the incomming border /// The number of frame the previous detected border NOT matched the incomming border
unsigned _inconsistentCnt; unsigned _inconsistentCnt;
}; };
} // end namespace hyperion } // end namespace hyperion

View File

@ -106,7 +106,7 @@ private:
/// @param[in] enableBlackBorderDetector Flag indicating if the blacborder detector should be enabled /// @param[in] enableBlackBorderDetector Flag indicating if the blacborder detector should be enabled
/// @param[in] blackborderThreshold The threshold which the blackborder detector should use /// @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 /// Performs black-border detection (if enabled) on the given image

View File

@ -33,7 +33,7 @@ public:
/// @param[in] enableBlackBorderDetector Flag indicating if the blacborder detector should be enabled /// @param[in] enableBlackBorderDetector Flag indicating if the blacborder detector should be enabled
/// @param[in] blackborderThreshold The threshold which the blackborder detector should use /// @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. /// Creates a new ImageProcessor. The onwership of the processor is transferred to the caller.
@ -46,9 +46,6 @@ private:
/// The Led-string specification /// The Led-string specification
LedString _ledString; LedString _ledString;
/// Flag indicating if the black border detector should be used // Reference to the blackborder json configuration values
bool _enableBlackBorderDetector; Json::Value _blackborderConfig;
/// Threshold for the blackborder detector [0 .. 255]
uint8_t _blackborderThreshold;
}; };

View File

@ -1,11 +1,26 @@
#include <iostream>
// BlackBorders includes // BlackBorders includes
#include <blackborder/BlackBorderDetector.h> #include <blackborder/BlackBorderDetector.h>
using namespace hyperion; using namespace hyperion;
BlackBorderDetector::BlackBorderDetector(uint8_t blackborderThreshold) : BlackBorderDetector::BlackBorderDetector(double threshold) :
_blackborderThreshold(blackborderThreshold) _blackborderThreshold(calculateThreshold(threshold))
{ {
// empty // 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;
}

View File

@ -1,23 +1,31 @@
//#include <iostream> //*
#include <iostream>
/*
#include <iomanip>
using std::cout;
using std::endl;
using std::setw;
using std::left;
//*/
// Blackborder includes // Blackborder includes
#include <blackborder/BlackBorderProcessor.h> #include <blackborder/BlackBorderProcessor.h>
using namespace hyperion; using namespace hyperion;
BlackBorderProcessor::BlackBorderProcessor(const unsigned unknownFrameCnt, BlackBorderProcessor::BlackBorderProcessor(const Json::Value &blackborderConfig) :
const unsigned borderFrameCnt, _unknownSwitchCnt(blackborderConfig.get("unknownFrameCnt", 600).asUInt()),
const unsigned blurRemoveCnt, _borderSwitchCnt(blackborderConfig.get("borderFrameCnt", 50).asUInt()),
uint8_t blackborderThreshold) : _maxInconsistentCnt(blackborderConfig.get("maxInconsistentCnt", 10).asUInt()),
_unknownSwitchCnt(unknownFrameCnt), _blurRemoveCnt(blackborderConfig.get("blurRemoveCnt", 1).asUInt()),
_borderSwitchCnt(borderFrameCnt), _detectionMode(blackborderConfig.get("mode", "default").asString()),
_blurRemoveCnt(blurRemoveCnt), _detector(blackborderConfig.get("threshold", 0.01).asDouble()),
_detector(blackborderThreshold),
_currentBorder({true, -1, -1}), _currentBorder({true, -1, -1}),
_previousDetectedBorder({true, -1, -1}), _previousDetectedBorder({true, -1, -1}),
_consistentCnt(0), _consistentCnt(0),
_inconsistentCnt(10) _inconsistentCnt(10)
{ {
std::cout << "DETECTION MODE:" << _detectionMode << std::endl;
// empty // 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 // 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 // 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 // set the consistency counter
if (newDetectedBorder == _previousDetectedBorder) if (newDetectedBorder == _previousDetectedBorder)
@ -50,7 +58,7 @@ bool BlackBorderProcessor::updateBorder(const BlackBorder & newDetectedBorder)
else else
{ {
++_inconsistentCnt; ++_inconsistentCnt;
if (_inconsistentCnt <= 10)// few inconsistent frames if (_inconsistentCnt <= _maxInconsistentCnt)// only few inconsistent frames
{ {
//discard the newDetectedBorder -> keep the consistent count for previousDetectedBorder //discard the newDetectedBorder -> keep the consistent count for previousDetectedBorder
return false; return false;

View File

@ -282,8 +282,8 @@ Hyperion::Hyperion(const Json::Value &jsonConfig) :
// initialize the image processor factory // initialize the image processor factory
ImageProcessorFactory::getInstance().init( ImageProcessorFactory::getInstance().init(
_ledString, _ledString,
jsonConfig["blackborderdetector"].get("enable", true).asBool(), jsonConfig["blackborderdetector"]
jsonConfig["blackborderdetector"].get("threshold", 0.01).asDouble()); );
// initialize the color smoothing filter // initialize the color smoothing filter
_device = createColorSmoothing(jsonConfig["color"]["smoothing"], _device); _device = createColorSmoothing(jsonConfig["color"]["smoothing"], _device);

View File

@ -8,10 +8,11 @@
using namespace hyperion; 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), _ledString(ledString),
_enableBlackBorderRemoval(enableBlackBorderDetector), _enableBlackBorderRemoval(blackborderConfig.get("enable", true).asBool()),
_borderProcessor(new BlackBorderProcessor(600, 50, 1, blackborderThreshold)), _borderProcessor(new BlackBorderProcessor(blackborderConfig) ),
_imageToLeds(nullptr) _imageToLeds(nullptr)
{ {
// empty // empty

View File

@ -13,25 +13,13 @@ ImageProcessorFactory& ImageProcessorFactory::getInstance()
return instance; return instance;
} }
void ImageProcessorFactory::init(const LedString& ledString, bool enableBlackBorderDetector, double blackborderThreshold) void ImageProcessorFactory::init(const LedString& ledString, const Json::Value & blackborderConfig)
{ {
_ledString = ledString; _ledString = ledString;
_enableBlackBorderDetector = enableBlackBorderDetector; _blackborderConfig = blackborderConfig;
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;
}
} }
ImageProcessor* ImageProcessorFactory::newImageProcessor() const ImageProcessor* ImageProcessorFactory::newImageProcessor() const
{ {
return new ImageProcessor(_ledString, _enableBlackBorderDetector, _blackborderThreshold); return new ImageProcessor(_ledString, _blackborderConfig);
} }

View File

@ -1,9 +1,7 @@
// STL includes // STL includes
#include <cstring> #include <cstring>
#include <cstdio> #include <cstdio>
#include <iostream> #include <iostream>
#include <algorithm>
// Linux includes // Linux includes
#include <fcntl.h> #include <fcntl.h>
@ -19,27 +17,46 @@ LedDeviceAPA102::LedDeviceAPA102(const std::string& outputDevice, const unsigned
// empty // 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<ColorRgb> &ledValues) int LedDeviceAPA102::write(const std::vector<ColorRgb> &ledValues)
{ {
const unsigned int startFrameSize = 4; const unsigned int startFrameSize = APA102_START_FRAME_BYTES;
const unsigned int endFrameSize = std::max<unsigned int>(((ledValues.size() + 15) / 16), 4); const unsigned int ledsCount = ledValues.size() ;
const unsigned int mLedCount = (ledValues.size() * 4) + startFrameSize + endFrameSize; const unsigned int ledsSize = ledsCount * APA102_LED_BYTES ;
if(_ledBuffer.size() != mLedCount){ const unsigned int endFrameBits = APA102_END_FRAME_BITS(ledsCount) ;
_ledBuffer.resize(mLedCount, 0xFF); const unsigned int endFrameSize = APA102_END_FRAME_BYTES(ledsCount) ;
_ledBuffer[0] = 0x00; const unsigned int transferSize = startFrameSize + ledsSize + endFrameSize ;
_ledBuffer[1] = 0x00;
_ledBuffer[2] = 0x00; if(_ledBuffer.size() != transferSize){
_ledBuffer[3] = 0x00; _ledBuffer.resize(transferSize, 0x00);
} }
for (unsigned iLed=1; iLed<=ledValues.size(); ++iLed) { unsigned idx = 0, i;
const ColorRgb& rgb = ledValues[iLed-1]; for (i=0; i<APA102_START_FRAME_BYTES; i++) {
_ledBuffer[iLed*4] = 0xFF; _ledBuffer[idx++] = 0x00 ;
_ledBuffer[iLed*4+1] = rgb.red;
_ledBuffer[iLed*4+2] = rgb.green;
_ledBuffer[iLed*4+3] = rgb.blue;
} }
for (i=0; i<ledsCount; i++) {
const ColorRgb& rgb = ledValues[i];
_ledBuffer[idx++] = APA102_LED_HEADER + APA102_LED_MAX_INTENSITY;
_ledBuffer[idx++] = rgb.red;
_ledBuffer[idx++] = rgb.green;
_ledBuffer[idx++] = rgb.blue;
}
for(i=0; i<endFrameSize; i++)
_ledBuffer[idx++] = 0x00 ;
return writeBytes(_ledBuffer.size(), _ledBuffer.data()); return writeBytes(_ledBuffer.size(), _ledBuffer.data());
} }

View File

@ -246,9 +246,10 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
} }
else if (type == "fadecandy") else if (type == "fadecandy")
{ {
const std::string host = deviceConfig.get("output", "127.0.0.1").asString(); const std::string host = deviceConfig.get("output", "127.0.0.1").asString();
const uint16_t port = deviceConfig.get("port", 7890).asInt(); const uint16_t port = deviceConfig.get("port", 7890).asInt();
device = new LedDeviceFadeCandy(host,port); const uint16_t channel = deviceConfig.get("channel", 0).asInt();
device = new LedDeviceFadeCandy(host, port, channel);
} }
else if (type == "tpm2") else if (type == "tpm2")
{ {

View File

@ -1,16 +1,15 @@
#include "LedDeviceFadeCandy.h" #include "LedDeviceFadeCandy.h"
static const unsigned MAX_NUM_LEDS = 512; static const unsigned MAX_NUM_LEDS = 10000; // OPC can handle 21845 leds - in theory, fadecandy device should handle 10000 leds
static const unsigned OPC_BROADCAST = 0; // OPC broadcast channel static const unsigned OPC_SET_PIXELS = 0; // OPC command codes
static const unsigned OPC_SET_PIXELS = 0; // OPC command codes static const unsigned OPC_HEADER_SIZE = 4; // OPC header size
static const unsigned OPC_HEADER_SIZE = 4; // OPC header size
LedDeviceFadeCandy::LedDeviceFadeCandy(const std::string& host, const uint16_t port) : LedDeviceFadeCandy::LedDeviceFadeCandy(const std::string& host, const uint16_t port, const unsigned channel) :
_host(host), _port(port) _host(host), _port(port), _channel(channel)
{ {
_opc_data.resize( OPC_HEADER_SIZE ); _opc_data.resize( OPC_HEADER_SIZE );
_opc_data[0] = OPC_BROADCAST; _opc_data[0] = channel;
_opc_data[1] = OPC_SET_PIXELS; _opc_data[1] = OPC_SET_PIXELS;
_opc_data[2] = 0; _opc_data[2] = 0;
_opc_data[3] = 0; _opc_data[3] = 0;
@ -32,11 +31,9 @@ bool LedDeviceFadeCandy::isConnected()
bool LedDeviceFadeCandy::tryConnect() bool LedDeviceFadeCandy::tryConnect()
{ {
if ( _client.state() == QAbstractSocket::UnconnectedState ) { if ( _client.state() == QAbstractSocket::UnconnectedState ) {
qDebug("connecting to %s %i",_host.c_str(),_port);
_client.connectToHost( _host.c_str(), _port); _client.connectToHost( _host.c_str(), _port);
if ( _client.waitForConnected(1000) ) if ( _client.waitForConnected(1000) )
qDebug("connected"); qDebug("fadecandy/opc: connected to %s:%i on channel %i", _host.c_str(), _port, _channel);
} }
return isConnected(); return isConnected();
@ -51,7 +48,7 @@ int LedDeviceFadeCandy::write( const std::vector<ColorRgb> & ledValues )
if (nrLedValues > MAX_NUM_LEDS) 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; return -1;
} }

View File

@ -23,7 +23,7 @@ public:
/// @param host The ip address/host name of fadecandy/opc server /// @param host The ip address/host name of fadecandy/opc server
/// @param port The port to use (fadecandy default is 7890) /// @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 /// Destructor of the LedDevice; closes the tcp client
@ -46,6 +46,7 @@ private:
QTcpSocket _client; QTcpSocket _client;
const std::string _host; const std::string _host;
const uint16_t _port; const uint16_t _port;
const unsigned _channel;
QByteArray _opc_data; QByteArray _opc_data;
/// try to establish connection to opc server, if not connected yet /// try to establish connection to opc server, if not connected yet

View File

@ -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); QString url = QString("http://%1/api/%2/%3").arg(host).arg(username).arg(route);
// Perfrom request // Perfrom request
QNetworkRequest request(url); 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. // Connect finished signal to quit slot of the loop.
QEventLoop loop; QEventLoop loop;
loop.connect(reply, SIGNAL(finished()), SLOT(quit())); loop.connect(reply, SIGNAL(finished()), SLOT(quit()));

View File

@ -126,20 +126,34 @@ int main(int argc, char** argv)
// Get the parameters for the bootsequence // Get the parameters for the bootsequence
const std::string effectName = effectConfig["effect"].asString(); const std::string effectName = effectConfig["effect"].asString();
const unsigned duration_ms = effectConfig["duration_ms"].asUInt(); const unsigned duration_ms = effectConfig["duration_ms"].asUInt();
const int priority = 0; 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")) if (effectConfig.isMember("args"))
{ {
const Json::Value effectConfigArgs = effectConfig["args"]; const Json::Value effectConfigArgs = effectConfig["args"];
if (hyperion.setEffect(effectName, effectConfigArgs, priority, duration_ms) == 0) 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 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 else

View File

@ -44,11 +44,13 @@ Image<ColorRgb> createImage(unsigned width, unsigned height, unsigned topBorder,
int main() int main()
{ {
unsigned unknownCnt = 600; // unsigned unknownCnt = 600;
unsigned borderCnt = 50; 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 // Start with 'no border' detection
Image<ColorRgb> noBorderImage = createImage(64, 64, 0, 0); Image<ColorRgb> noBorderImage = createImage(64, 64, 0, 0);