feat: Integrate WledClient into grabberconfigurator for direct WLED communication
This commit is contained in:
parent
478828bdcf
commit
60bcfb043d
@ -123,7 +123,7 @@ void HyperionProcessor::buildLedMap(int imageWidth, int imageHeight, const Black
|
||||
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 from leftmost 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;
|
||||
@ -168,4 +168,4 @@ QColor HyperionProcessor::getAverageColor(const QImage &image, const QVector<QPo
|
||||
}
|
||||
|
||||
return QColor(r / pixels.size(), g / pixels.size(), b / pixels.size());
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@ -4,12 +4,15 @@
|
||||
#include <csignal>
|
||||
#include <QJsonObject>
|
||||
#include <QTextStream>
|
||||
#include <QTimer>
|
||||
#include "hyperiongrabber.h"
|
||||
#include "HyperionProcessor.h"
|
||||
#include "LedColorMapping.h"
|
||||
#include "wledclient.h"
|
||||
|
||||
// ANSI escape code to clear the screen
|
||||
const QString ANSI_RESET = "\033[0m";
|
||||
const QString ANSI_CLEAR_SCREEN = "\033[2J\033[1;1H";
|
||||
|
||||
// Function to get a printable block with a specific background color
|
||||
QString getAnsiColorBlock(const QColor& color) {
|
||||
@ -20,12 +23,64 @@ QString getAnsiColorBlock(const QColor& color) {
|
||||
return QString("\033[48;2;%1;%2;%3m ").arg(color.red()).arg(color.green()).arg(color.blue());
|
||||
}
|
||||
|
||||
void printLedColorsAsBoxes(const QVector<QColor>& ledColors) {
|
||||
void printAsciiLayout(const QVector<QColor>& ledColors, const LedLayout& layout) {
|
||||
QTextStream cout(stdout);
|
||||
for (const QColor& color : ledColors) {
|
||||
cout << getAnsiColorBlock(color);
|
||||
cout << ANSI_CLEAR_SCREEN;
|
||||
|
||||
// Calculate grid dimensions based on the maximum extent of LEDs on each side
|
||||
int max_h_extent = qMax(layout.bottom, layout.top);
|
||||
int max_v_extent = qMax(layout.left, layout.right);
|
||||
|
||||
// Add 2 for the corner LEDs (one for left side, one for right side)
|
||||
int grid_width = max_h_extent + 2;
|
||||
// Add 2 for the corner LEDs (one for top side, one for bottom side)
|
||||
int grid_height = max_v_extent + 2;
|
||||
|
||||
// Create a 2D grid to store the color blocks
|
||||
QVector<QVector<QString>> grid(grid_height, QVector<QString>(grid_width, " "));
|
||||
|
||||
// Populate the grid with LED colors
|
||||
int currentLedIndex = 0;
|
||||
|
||||
// Bottom LEDs (left to right)
|
||||
for (int i = 0; i < layout.bottom; ++i) {
|
||||
if (currentLedIndex < ledColors.size()) {
|
||||
grid[grid_height - 1][1 + i] = getAnsiColorBlock(ledColors.value(currentLedIndex));
|
||||
}
|
||||
currentLedIndex++;
|
||||
}
|
||||
|
||||
// Right LEDs (bottom to top)
|
||||
for (int i = 0; i < layout.right; ++i) {
|
||||
if (currentLedIndex < ledColors.size()) {
|
||||
grid[grid_height - 2 - i][grid_width - 1] = getAnsiColorBlock(ledColors.value(currentLedIndex));
|
||||
}
|
||||
currentLedIndex++;
|
||||
}
|
||||
|
||||
// Top LEDs (right to left)
|
||||
for (int i = 0; i < layout.top; ++i) {
|
||||
if (currentLedIndex < ledColors.size()) {
|
||||
grid[0][grid_width - 2 - i] = getAnsiColorBlock(ledColors.value(currentLedIndex));
|
||||
}
|
||||
currentLedIndex++;
|
||||
}
|
||||
|
||||
// Left LEDs (top to bottom)
|
||||
for (int i = 0; i < layout.left; ++i) {
|
||||
if (currentLedIndex < ledColors.size()) {
|
||||
grid[1 + i][0] = getAnsiColorBlock(ledColors.value(currentLedIndex));
|
||||
}
|
||||
currentLedIndex++;
|
||||
}
|
||||
|
||||
// Print the grid
|
||||
for (int r = 0; r < grid_height; ++r) {
|
||||
for (int c = 0; c < grid_width; ++c) {
|
||||
cout << grid[r][c];
|
||||
}
|
||||
cout << ANSI_RESET << "\n";
|
||||
}
|
||||
cout << ANSI_RESET << "\n";
|
||||
cout.flush();
|
||||
}
|
||||
|
||||
@ -33,6 +88,7 @@ void printLedColorsAsBoxes(const QVector<QColor>& ledColors) {
|
||||
static HyperionGrabber *grabber = nullptr;
|
||||
static QApplication *app = nullptr;
|
||||
static HyperionProcessor *processor = nullptr;
|
||||
static WledClient *wledClient = nullptr;
|
||||
|
||||
void quit(int) {
|
||||
if (grabber != nullptr) {
|
||||
@ -41,6 +97,9 @@ void quit(int) {
|
||||
if (processor != nullptr) {
|
||||
delete processor;
|
||||
}
|
||||
if (wledClient != nullptr) {
|
||||
delete wledClient;
|
||||
}
|
||||
if (app != nullptr) {
|
||||
app->quit();
|
||||
}
|
||||
@ -51,21 +110,42 @@ class GrabberConfigurator : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GrabberConfigurator(const QHash<QString, QString> &opts, const LedLayout& layout, const QJsonObject& config, QObject *parent = nullptr)
|
||||
GrabberConfigurator(const QHash<QString, QString> &opts, const LedLayout& layout, const QJsonObject& config, const QString& wledAddress, quint16 wledPort, QObject *parent = nullptr)
|
||||
: QObject(parent), _layout(layout)
|
||||
{
|
||||
grabber = new HyperionGrabber(opts);
|
||||
processor = new HyperionProcessor(layout, config);
|
||||
connect(grabber, &HyperionGrabber::imageReady, this, &GrabberConfigurator::processFrame);
|
||||
wledClient = new WledClient(wledAddress, wledPort, this);
|
||||
|
||||
// Connect grabber to a new slot that will trigger the timer
|
||||
connect(grabber, &HyperionGrabber::imageReady, this, &GrabberConfigurator::onImageReady);
|
||||
|
||||
_timer = new QTimer(this);
|
||||
_timer->setInterval(1000); // 1 second
|
||||
connect(_timer, &QTimer::timeout, this, &GrabberConfigurator::processCurrentFrame);
|
||||
_timer->start();
|
||||
}
|
||||
|
||||
public slots:
|
||||
void processFrame(const QImage &image) {
|
||||
qDebug() << "GrabberConfigurator::processFrame called.";
|
||||
if (image.isNull()) return;
|
||||
void onImageReady(const QImage &image) {
|
||||
_currentImage = image; // Store the latest image
|
||||
}
|
||||
|
||||
void processCurrentFrame() {
|
||||
QTextStream cout(stdout);
|
||||
cout << "\n\n\n"; // Print newlines for separation
|
||||
qDebug() << "GrabberConfigurator::processCurrentFrame called.";
|
||||
|
||||
if (_currentImage.isNull()) {
|
||||
qDebug() << "No image available yet.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Process the image with HyperionProcessor
|
||||
QVector<QColor> ledColors = processor->process(image);
|
||||
QVector<QColor> ledColors = processor->process(_currentImage);
|
||||
|
||||
// Send colors to WLED
|
||||
wledClient->setLedsColor(ledColors);
|
||||
|
||||
// Debugging: Print some sample LED colors
|
||||
if (!ledColors.isEmpty()) {
|
||||
@ -76,46 +156,14 @@ public slots:
|
||||
if (ledColors.size() > _layout.bottom + _layout.right) qDebug() << " LED (bottom+right+1):" << ledColors.at(_layout.bottom + _layout.right).name();
|
||||
}
|
||||
|
||||
// Get the processed image dimensions and border info
|
||||
QSize processedImageSize = processor->getLastImageSize();
|
||||
BlackBorder border = processor->getLastBorder();
|
||||
|
||||
// Calculate the content rectangle
|
||||
int xOffset = border.verticalSize;
|
||||
int yOffset = border.horizontalSize;
|
||||
int contentWidth = processedImageSize.width() - (2 * border.verticalSize);
|
||||
int contentHeight = processedImageSize.height() - (2 * border.horizontalSize);
|
||||
|
||||
if (contentWidth > 0 && contentHeight > 0) {
|
||||
// QImage croppedImage = image.copy(xOffset, yOffset, contentWidth, contentHeight);
|
||||
|
||||
// Debugging: Print ledMap details
|
||||
const QVector<QVector<QPoint>>& ledMap = processor->getLedMap();
|
||||
qDebug() << "ledMap size:" << ledMap.size();
|
||||
|
||||
for (int i = 0; i < ledColors.size(); ++i) {
|
||||
const QColor& color = ledColors.at(i);
|
||||
const QVector<QPoint>& pixels = ledMap.at(i);
|
||||
qDebug() << " LED" << i << "pixels size:" << pixels.size();
|
||||
|
||||
// Debugging: Print rectangle dimensions (if needed, can be re-enabled)
|
||||
// int minX = image.width(); int minY = image.height(); int maxX = 0; int maxY = 0;
|
||||
// for (const QPoint& p : pixels) { minX = qMin(minX, p.x()); minY = qMin(minY, p.y()); maxX = qMax(maxX, p.x()); maxY = qMax(maxY, p.y()); }
|
||||
// int rectMinX = minX - xOffset; int rectMinY = minY - yOffset;
|
||||
// int rectWidth = qMax(1, maxX - minX + 1); int rectHeight = qMax(1, maxY - minY + 1);
|
||||
// qDebug() << "LED" << i << ": Rect(" << rectMinX << "," << rectMinY << "," << rectWidth << "," << rectHeight << ") Color:" << color.name();
|
||||
}
|
||||
|
||||
// Print LED colors as a line of boxes in the terminal
|
||||
printLedColorsAsBoxes(ledColors);
|
||||
|
||||
} else {
|
||||
qDebug() << "No content to process after black border removal.";
|
||||
}
|
||||
// Remove terminal visualization
|
||||
// printAsciiLayout(ledColors, _layout);
|
||||
}
|
||||
|
||||
private:
|
||||
LedLayout _layout; // Still needed for HyperionProcessor constructor
|
||||
QTimer *_timer;
|
||||
QImage _currentImage;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@ -143,10 +191,23 @@ int main(int argc, char *argv[])
|
||||
parser.addOption(QCommandLineOption(QStringList() << "leds-left", "Number of LEDs on the left edge.", "count", "20"));
|
||||
|
||||
// Add options for the processor
|
||||
parser.addOption(QCommandLineOption(QStringList() << "bbt", "Black border threshold (0.0 to 1.0).", "threshold", "0.1"));
|
||||
parser.addOption(QCommandLineOption(QStringList() << "bbt", "Black border threshold (0.0 to 1.0).", "threshold", "0.2")); // Increased threshold
|
||||
|
||||
// Add WLED options
|
||||
parser.addOption(QCommandLineOption(QStringList() << "a" << "address", "IP address of the WLED device.", "address"));
|
||||
parser.addOption(QCommandLineOption(QStringList() << "p" << "port", "UDP port of the WLED device.", "port", "21324"));
|
||||
|
||||
parser.process(*app);
|
||||
|
||||
// Validate WLED address
|
||||
if (!parser.isSet("address")) {
|
||||
qCritical() << "Error: WLED IP address not provided. Use -a or --address.";
|
||||
parser.showHelp(1);
|
||||
}
|
||||
|
||||
QString wledAddress = parser.value("address");
|
||||
quint16 wledPort = parser.value("port").toUShort();
|
||||
|
||||
QHash<QString, QString> grabberOpts;
|
||||
grabberOpts.insert("scale", parser.value("scale"));
|
||||
grabberOpts.insert("frameskip", parser.value("frameskip"));
|
||||
@ -160,18 +221,11 @@ int main(int argc, char *argv[])
|
||||
QJsonObject processorConfig;
|
||||
processorConfig["blackBorderThreshold"] = parser.value("bbt").toDouble();
|
||||
|
||||
// No QLabel or QWidget needed for this visualization
|
||||
// QWidget window;
|
||||
// QLabel label;
|
||||
// label.setParent(&window);
|
||||
// label.setScaledContents(true);
|
||||
// window.show();
|
||||
GrabberConfigurator configurator(grabberOpts, layout, processorConfig, wledAddress, wledPort);
|
||||
|
||||
GrabberConfigurator configurator(grabberOpts, layout, processorConfig);
|
||||
|
||||
qInfo() << "Starting GrabberConfigurator. A line of colored boxes should appear in the terminal. Press Ctrl+C to exit.";
|
||||
qInfo() << "Starting GrabberConfigurator. Sending LED colors to WLED device." << wledAddress << ":" << wledPort << ". Press Ctrl+C to exit.";
|
||||
|
||||
return app->exec();
|
||||
}
|
||||
|
||||
#include "grabberconfigurator.moc"
|
||||
#include "grabberconfigurator.moc"
|
||||
|
||||
@ -38,16 +38,19 @@ template <> constexpr inline auto GrabberConfigurator::qt_create_metaobjectdata<
|
||||
namespace QMC = QtMocConstants;
|
||||
QtMocHelpers::StringRefStorage qt_stringData {
|
||||
"GrabberConfigurator",
|
||||
"processFrame",
|
||||
"onImageReady",
|
||||
"",
|
||||
"image"
|
||||
"image",
|
||||
"processCurrentFrame"
|
||||
};
|
||||
|
||||
QtMocHelpers::UintData qt_methods {
|
||||
// Slot 'processFrame'
|
||||
// Slot 'onImageReady'
|
||||
QtMocHelpers::SlotData<void(const QImage &)>(1, 2, QMC::AccessPublic, QMetaType::Void, {{
|
||||
{ QMetaType::QImage, 3 },
|
||||
}}),
|
||||
// Slot 'processCurrentFrame'
|
||||
QtMocHelpers::SlotData<void()>(4, 2, QMC::AccessPublic, QMetaType::Void),
|
||||
};
|
||||
QtMocHelpers::UintData qt_properties {
|
||||
};
|
||||
@ -71,7 +74,8 @@ void GrabberConfigurator::qt_static_metacall(QObject *_o, QMetaObject::Call _c,
|
||||
auto *_t = static_cast<GrabberConfigurator *>(_o);
|
||||
if (_c == QMetaObject::InvokeMetaMethod) {
|
||||
switch (_id) {
|
||||
case 0: _t->processFrame((*reinterpret_cast< std::add_pointer_t<QImage>>(_a[1]))); break;
|
||||
case 0: _t->onImageReady((*reinterpret_cast< std::add_pointer_t<QImage>>(_a[1]))); break;
|
||||
case 1: _t->processCurrentFrame(); break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
@ -96,14 +100,14 @@ int GrabberConfigurator::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
||||
if (_id < 0)
|
||||
return _id;
|
||||
if (_c == QMetaObject::InvokeMetaMethod) {
|
||||
if (_id < 1)
|
||||
if (_id < 2)
|
||||
qt_static_metacall(this, _c, _id, _a);
|
||||
_id -= 1;
|
||||
_id -= 2;
|
||||
}
|
||||
if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
|
||||
if (_id < 1)
|
||||
if (_id < 2)
|
||||
*reinterpret_cast<QMetaType *>(_a[0]) = QMetaType();
|
||||
_id -= 1;
|
||||
_id -= 2;
|
||||
}
|
||||
return _id;
|
||||
}
|
||||
|
||||
77731
output.txt
Normal file
77731
output.txt
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user