172 lines
6.7 KiB
C++
172 lines
6.7 KiB
C++
#include "HyperionProcessor.h"
|
|
#include <QDebug>
|
|
|
|
HyperionProcessor::HyperionProcessor(const LedLayout &layout, const QJsonObject &config, QObject *parent)
|
|
: QObject(parent),
|
|
_layout(layout),
|
|
_config(config),
|
|
_borderDetector(config.value("blackBorderThreshold").toDouble(0.1)),
|
|
_lastImageSize(0, 0)
|
|
{
|
|
}
|
|
|
|
QVector<QColor> HyperionProcessor::process(const QImage &image)
|
|
{
|
|
qDebug() << "HyperionProcessor::process called.";
|
|
if (image.isNull()) {
|
|
return QVector<QColor>();
|
|
}
|
|
|
|
// 1. Detect black borders
|
|
BlackBorder currentBorder = _borderDetector.process(image);
|
|
|
|
// 2. Check if we need to rebuild the LED map
|
|
if (image.size() != _lastImageSize ||
|
|
currentBorder.horizontalSize != _lastBorder.horizontalSize ||
|
|
currentBorder.verticalSize != _lastBorder.verticalSize)
|
|
{
|
|
qDebug() << "Change detected. Rebuilding LED map.";
|
|
qDebug() << "New image size:" << image.size();
|
|
qDebug() << "New border - H:" << currentBorder.horizontalSize << "V:" << currentBorder.verticalSize;
|
|
buildLedMap(image.width(), image.height(), currentBorder);
|
|
_lastImageSize = image.size();
|
|
_lastBorder = currentBorder;
|
|
}
|
|
|
|
// 3. Calculate the colors
|
|
return calculateLedColors(image);
|
|
}
|
|
|
|
void HyperionProcessor::buildLedMap(int imageWidth, int imageHeight, const BlackBorder &border)
|
|
{
|
|
_ledMap.clear();
|
|
|
|
int contentWidth = imageWidth - (2 * border.verticalSize);
|
|
int contentHeight = imageHeight - (2 * border.horizontalSize);
|
|
|
|
qDebug() << "buildLedMap: imageWidth=" << imageWidth << "imageHeight=" << imageHeight;
|
|
qDebug() << "buildLedMap: border.verticalSize=" << border.verticalSize << "border.horizontalSize=" << border.horizontalSize;
|
|
qDebug() << "buildLedMap: contentWidth=" << contentWidth << "contentHeight=" << contentHeight;
|
|
|
|
if (contentWidth <= 0 || contentHeight <= 0) {
|
|
qWarning() << "No content to process after black border removal. Content W:" << contentWidth << "H:" << contentHeight;
|
|
return;
|
|
}
|
|
|
|
int totalLeds = _layout.left + _layout.right + _layout.top + _layout.bottom;
|
|
qDebug() << "buildLedMap: totalLeds=" << totalLeds;
|
|
_ledMap.resize(totalLeds);
|
|
|
|
// Define sampling thickness (e.g., 5% of the content height/width, minimum 1 pixel)
|
|
int hSamplingThickness = qMax(1, contentHeight / 20); // 5% of content height
|
|
int vSamplingThickness = qMax(1, contentWidth / 20); // 5% of content width
|
|
|
|
int ledIndex = 0;
|
|
|
|
// Bottom LEDs
|
|
for (int i = 0; i < _layout.bottom; ++i, ++ledIndex) {
|
|
double hScanStart = double(i) / _layout.bottom;
|
|
double hScanEnd = double(i + 1) / _layout.bottom;
|
|
int xStart = border.verticalSize + int(hScanStart * contentWidth);
|
|
int xEnd = border.verticalSize + int(hScanEnd * contentWidth);
|
|
int yStart = border.horizontalSize + contentHeight - hSamplingThickness; // Sample from bottom region
|
|
int yEnd = border.horizontalSize + contentHeight; // To the very bottom
|
|
|
|
qDebug() << "LED" << ledIndex << ": xStart=" << xStart << "xEnd=" << xEnd << "yStart=" << yStart << "yEnd=" << yEnd;
|
|
|
|
for (int x = xStart; x < xEnd; ++x) {
|
|
for (int y = yStart; y < yEnd; ++y) {
|
|
_ledMap[ledIndex].append(QPoint(x, y));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Right LEDs
|
|
for (int i = 0; i < _layout.right; ++i, ++ledIndex) {
|
|
double vScanStart = double(i) / _layout.right;
|
|
double vScanEnd = double(i + 1) / _layout.right;
|
|
int yStart = border.horizontalSize + int((1.0 - vScanEnd) * contentHeight); // Sample from top of region
|
|
int yEnd = border.horizontalSize + int((1.0 - vScanStart) * contentHeight); // To bottom of region
|
|
int xStart = border.verticalSize + contentWidth - vSamplingThickness; // Sample from rightmost region
|
|
int xEnd = border.verticalSize + contentWidth; // To the very right
|
|
|
|
qDebug() << "LED" << ledIndex << ": xStart=" << xStart << "xEnd=" << xEnd << "yStart=" << yStart << "yEnd=" << yEnd;
|
|
|
|
for (int x = xStart; x < xEnd; ++x) {
|
|
for (int y = yStart; y < yEnd; ++y) {
|
|
_ledMap[ledIndex].append(QPoint(x, y));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Top LEDs
|
|
for (int i = 0; i < _layout.top; ++i, ++ledIndex) {
|
|
double hScanStart = double(i) / _layout.top;
|
|
double hScanEnd = double(i + 1) / _layout.top;
|
|
int xStart = border.verticalSize + int((1.0 - hScanEnd) * contentWidth); // Sample from right of region
|
|
int xEnd = border.verticalSize + int((1.0 - hScanStart) * contentWidth); // To left of region
|
|
int yStart = border.horizontalSize; // Sample from top region
|
|
int yEnd = border.horizontalSize + hSamplingThickness; // To bottom of region
|
|
|
|
qDebug() << "LED" << ledIndex << ": xStart=" << xStart << "xEnd=" << xEnd << "yStart=" << yStart << "yEnd=" << yEnd;
|
|
|
|
for (int x = xStart; x < xEnd; ++x) {
|
|
for (int y = yStart; y < yEnd; ++y) {
|
|
_ledMap[ledIndex].append(QPoint(x, y));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Left LEDs
|
|
for (int i = 0; i < _layout.left; ++i, ++ledIndex) {
|
|
double vScanStart = double(i) / _layout.left;
|
|
double vScanEnd = double(i + 1) / _layout.left;
|
|
int yStart = border.horizontalSize + int(vScanStart * contentHeight); // Sample from top of region
|
|
int yEnd = border.horizontalSize + int(vScanEnd * contentHeight); // To bottom of region
|
|
int xStart = border.verticalSize; // Sample leftmost region
|
|
int xEnd = border.verticalSize + vSamplingThickness; // To right of region
|
|
|
|
qDebug() << "LED" << ledIndex << ": xStart=" << xStart << "xEnd=" << xEnd << "yStart=" << yStart << "yEnd=" << yEnd;
|
|
|
|
for (int x = xStart; x < xEnd; ++x) {
|
|
for (int y = yStart; y < yEnd; ++y) {
|
|
_ledMap[ledIndex].append(QPoint(x, y));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QVector<QColor> HyperionProcessor::calculateLedColors(const QImage &image)
|
|
{
|
|
qDebug() << "HyperionProcessor::calculateLedColors called.";
|
|
QVector<QColor> ledColors;
|
|
if (_ledMap.isEmpty()) {
|
|
return ledColors;
|
|
}
|
|
|
|
ledColors.reserve(_ledMap.size());
|
|
|
|
for (const auto &pixelList : _ledMap) {
|
|
ledColors.append(getAverageColor(image, pixelList));
|
|
}
|
|
|
|
return ledColors;
|
|
}
|
|
|
|
QColor HyperionProcessor::getAverageColor(const QImage &image, const QVector<QPoint> &pixels) const
|
|
{
|
|
if (pixels.isEmpty()) {
|
|
return Qt::black;
|
|
}
|
|
|
|
quint64 r = 0, g = 0, b = 0;
|
|
for (const QPoint &pt : pixels) {
|
|
QColor color = image.pixelColor(pt);
|
|
r += color.red();
|
|
g += color.green();
|
|
b += color.blue();
|
|
}
|
|
|
|
return QColor(r / pixels.size(), g / pixels.size(), b / pixels.size());
|
|
}
|