Added image flipping ability to MF Grabber

This commit is contained in:
Paulchen Panther
2021-01-24 12:16:16 +01:00
parent d5717af2df
commit 4254f36bba
18 changed files with 255 additions and 164 deletions

View File

@@ -1,7 +1,10 @@
#include "MFSourceReaderCB.h"
#include "grabber/MFGrabber.h"
MFGrabber::MFGrabber(const QString & device, unsigned width, unsigned height, unsigned fps, unsigned input, int pixelDecimation)
// Constants
namespace { const bool verbose = false; }
MFGrabber::MFGrabber(const QString & device, unsigned width, unsigned height, unsigned fps, unsigned input, int pixelDecimation, QString flipMode)
: Grabber("V4L2:"+device)
, _deviceName(device)
, _hr(S_FALSE)
@@ -30,6 +33,7 @@ MFGrabber::MFGrabber(const QString & device, unsigned width, unsigned height, un
setInput(input);
setWidthHeight(width, height);
setFramerate(fps);
setFlipMode(flipMode);
// setDeviceVideoStandard(device, videoStandard); // TODO
CoInitializeEx(0, COINIT_MULTITHREADED);
@@ -467,8 +471,8 @@ void MFGrabber::enumVideoCaptureDevices()
if (pixelformat != PixelFormat::NO_CHANGE)
{
int framerate = fr1/fr2;
QString sFrame = QString::number(framerate).rightJustified(2,' ');
QString displayResolutions = QString::number(w).rightJustified(4,' ') +"x"+ QString::number(h).rightJustified(4,' ');
QString sFrame = QString::number(framerate).rightJustified(2,' ').trimmed();
QString displayResolutions = QString::number(w).rightJustified(4,' ').trimmed() +"x"+ QString::number(h).rightJustified(4,' ').trimmed();
if (!properties.displayResolutions.contains(displayResolutions))
properties.displayResolutions << displayResolutions;
@@ -486,7 +490,7 @@ void MFGrabber::enumVideoCaptureDevices()
di.guid = format;
properties.valid.append(di);
Debug(_log, "%s %d x %d @ %d fps (%s)", QSTRING_CSTR(dev), di.x, di.y, di.fps, QSTRING_CSTR(pixelFormatToString(di.pf)));
DebugIf(verbose, _log, "%s %d x %d @ %d fps (%s)", QSTRING_CSTR(dev), di.x, di.y, di.fps, QSTRING_CSTR(pixelFormatToString(di.pf)));
}
}
@@ -553,12 +557,12 @@ void MFGrabber::process_image(const void *frameImageBuffer, int size)
for (unsigned int i=0; i < _threadManager._maxThreads && _threadManager._threads != nullptr; i++)
{
MFThread* _thread=_threadManager._threads[i];
MFThread* _thread = _threadManager._threads[i];
connect(_thread, SIGNAL(newFrame(unsigned int, const Image<ColorRgb> &,unsigned int)), this , SLOT(newThreadFrame(unsigned int, const Image<ColorRgb> &, unsigned int)));
}
}
for (unsigned int i=0;_threadManager.isActive() && i < _threadManager._maxThreads && _threadManager._threads != nullptr; i++)
for (unsigned int i = 0;_threadManager.isActive() && i < _threadManager._maxThreads && _threadManager._threads != nullptr; i++)
{
if ((_threadManager._threads[i]->isFinished() || !_threadManager._threads[i]->isRunning()))
{
@@ -566,11 +570,8 @@ void MFGrabber::process_image(const void *frameImageBuffer, int size)
if ( _threadManager._threads[i]->isBusy() == false)
{
MFThread* _thread = _threadManager._threads[i];
_thread->setup(i, _pixelFormat, (uint8_t *)frameImageBuffer, size, _width, _height, _lineLength, _subsamp, _cropLeft, _cropTop, _cropBottom, _cropRight, _videoMode, processFrameIndex, _pixelDecimation);
if (_threadManager._maxThreads > 1)
_threadManager._threads[i]->start();
_thread->setup(i, _pixelFormat, (uint8_t *)frameImageBuffer, size, _width, _height, _lineLength, _subsamp, _cropLeft, _cropTop, _cropBottom, _cropRight, _videoMode, _flipMode, processFrameIndex, _pixelDecimation);
_threadManager._threads[i]->start();
break;
}
}
@@ -586,7 +587,8 @@ void MFGrabber::setSignalThreshold(double redSignalThreshold, double greenSignal
_noSignalThresholdColor.blue = uint8_t(255*blueSignalThreshold);
_noSignalCounterThreshold = qMax(1, noSignalCounterThreshold);
Info(_log, "Signal threshold set to: {%d, %d, %d} and frames: %d", _noSignalThresholdColor.red, _noSignalThresholdColor.green, _noSignalThresholdColor.blue, _noSignalCounterThreshold );
if(_signalDetectionEnabled)
Info(_log, "Signal threshold set to: {%d, %d, %d} and frames: %d", _noSignalThresholdColor.red, _noSignalThresholdColor.green, _noSignalThresholdColor.blue, _noSignalCounterThreshold );
}
void MFGrabber::setSignalDetectionOffset(double horizontalMin, double verticalMin, double horizontalMax, double verticalMax)
@@ -599,7 +601,8 @@ void MFGrabber::setSignalDetectionOffset(double horizontalMin, double verticalMi
_x_frac_max = horizontalMax;
_y_frac_max = verticalMax;
Info(_log, "Signal detection area set to: %f,%f x %f,%f", _x_frac_min, _y_frac_min, _x_frac_max, _y_frac_max );
if(_signalDetectionEnabled)
Info(_log, "Signal detection area set to: %f,%f x %f,%f", _x_frac_min, _y_frac_min, _x_frac_max, _y_frac_max );
}
bool MFGrabber::start()
@@ -744,6 +747,12 @@ void MFGrabber::setPixelDecimation(int pixelDecimation)
_pixelDecimation = pixelDecimation;
}
void MFGrabber::setFlipMode(QString flipMode)
{
if(_flipMode != parseFlipMode(flipMode))
Grabber::setFlipMode(parseFlipMode(flipMode));
}
void MFGrabber::setDeviceVideoStandard(QString device, VideoStandard videoStandard)
{
if(_deviceName != device)

View File

@@ -3,26 +3,31 @@
volatile bool MFThread::_isActive = false;
MFThread::MFThread()
: _localData(nullptr)
: _isBusy(false)
, _semaphore(1)
, _localData(nullptr)
, _localDataSize(0)
, _decompress(nullptr)
, _scalingFactorsCount(0)
, _scalingFactors(nullptr)
, _isBusy(false)
, _semaphore(1)
, _transform(nullptr)
, _decompress(nullptr)
, _xform(nullptr)
, _imageResampler()
{
}
MFThread::~MFThread()
{
if (_decompress == nullptr)
if (_transform)
tjDestroy(_transform);
if (_decompress)
tjDestroy(_decompress);
if (_localData != NULL)
if (_localData)
{
free(_localData);
_localData = NULL;
_localData = nullptr;
_localDataSize = 0;
}
}
@@ -31,9 +36,9 @@ void MFThread::setup(
unsigned int threadIndex, PixelFormat pixelFormat, uint8_t* sharedData,
int size, int width, int height, int lineLength,
int subsamp, unsigned cropLeft, unsigned cropTop, unsigned cropBottom, unsigned cropRight,
VideoMode videoMode, int currentFrame, int pixelDecimation)
VideoMode videoMode, FlipMode flipMode, int currentFrame, int pixelDecimation)
{
_workerIndex = threadIndex;
_threadIndex = threadIndex;
_lineLength = lineLength;
_pixelFormat = pixelFormat;
_size = size;
@@ -44,26 +49,25 @@ void MFThread::setup(
_cropTop = cropTop;
_cropBottom = cropBottom;
_cropRight = cropRight;
_flipMode = flipMode;
_currentFrame = currentFrame;
_pixelDecimation = pixelDecimation;
_imageResampler.setVideoMode(videoMode);
_imageResampler.setFlipMode(_flipMode);
_imageResampler.setCropping(cropLeft, cropRight, cropTop, cropBottom);
_imageResampler.setHorizontalPixelDecimation(_pixelDecimation);
_imageResampler.setVerticalPixelDecimation(_pixelDecimation);
_imageResampler.setFlipMode(FlipMode::NO_CHANGE);
if (size > _localDataSize)
{
if (_localData != NULL)
{
free(_localData);
_localData = NULL;
_localDataSize = 0;
}
_localData = (uint8_t *) malloc(size+1);
if (_localData)
tjFree(_localData);
_localData = (uint8_t*)tjAlloc(size + 1);
_localDataSize = size;
}
memcpy(_localData, sharedData, size);
}
@@ -79,7 +83,7 @@ void MFThread::run()
{
Image<ColorRgb> image = Image<ColorRgb>();
_imageResampler.processImage(_localData, _width, _height, _lineLength, PixelFormat::BGR24, image);
emit newFrame(_workerIndex, image, _currentFrame);
emit newFrame(_threadIndex, image, _currentFrame);
}
}
}
@@ -108,11 +112,36 @@ void MFThread::noBusy()
void MFThread::processImageMjpeg()
{
if (_decompress == nullptr)
if (!_transform && _flipMode != FlipMode::NO_CHANGE)
{
_transform = tjInitTransform();
_xform = new tjtransform();
}
if (_flipMode == FlipMode::BOTH || _flipMode == FlipMode::HORIZONTAL)
{
_xform->op = TJXOP_HFLIP;
uint8_t* dstBuf = nullptr;
unsigned long dstSize = 0;
tjTransform(_transform, _localData, _size, 1, &dstBuf, &dstSize, _xform, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE);
_localData = dstBuf;
_size = dstSize;
}
if (_flipMode == FlipMode::BOTH || _flipMode == FlipMode::VERTICAL)
{
_xform->op = TJXOP_VFLIP;
uint8_t *dstBuf = nullptr;
unsigned long dstSize = 0;
tjTransform(_transform, _localData, _size, 1, &dstBuf, &dstSize, _xform, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE);
_localData = dstBuf;
_size = dstSize;
}
if (!_decompress)
{
_decompress = tjInitDecompress();
_scalingFactors = tjGetScalingFactors (&_scalingFactorsCount);
tjhandle handle=NULL;
_scalingFactors = tjGetScalingFactors(&_scalingFactorsCount);
}
if (tjDecompressHeader2(_decompress, _localData, _size, &_width, &_height, &_subsamp) != 0)
@@ -153,10 +182,10 @@ void MFThread::processImageMjpeg()
// got image, process it
if ( !(_cropLeft > 0 || _cropTop > 0 || _cropBottom > 0 || _cropRight > 0))
emit newFrame(_workerIndex, srcImage, _currentFrame);
emit newFrame(_threadIndex, srcImage, _currentFrame);
else
{
// calculate the output size
// calculate the output size
int outputWidth = (_width - _cropLeft - _cropRight);
int outputHeight = (_height - _cropTop - _cropBottom);
@@ -171,10 +200,12 @@ void MFThread::processImageMjpeg()
unsigned char* dest = (unsigned char*)destImage.memptr() + y*destImage.width()*3;
memcpy(dest, source, destImage.width()*3);
free(source);
source = nullptr;
free(dest);
dest = nullptr;
}
// emit
emit newFrame(_workerIndex, destImage, _currentFrame);
emit newFrame(_threadIndex, destImage, _currentFrame);
}
}

View File

@@ -5,9 +5,9 @@
// qt
#include <QTimer>
MFWrapper::MFWrapper(const QString &device, unsigned grabWidth, unsigned grabHeight, unsigned fps, unsigned input, int pixelDecimation )
MFWrapper::MFWrapper(const QString &device, unsigned grabWidth, unsigned grabHeight, unsigned fps, unsigned input, int pixelDecimation, QString flipMode)
: GrabberWrapper("V4L2:"+device, &_grabber, grabWidth, grabHeight, 10)
, _grabber(device, grabWidth, grabHeight, fps, input, pixelDecimation)
, _grabber(device, grabWidth, grabHeight, fps, input, pixelDecimation, flipMode)
{
_ggrabber = &_grabber;
@@ -123,6 +123,9 @@ void MFWrapper::handleSettingsUpdate(settings::type type, const QJsonDocument& c
// image size decimation
_grabber.setPixelDecimation(obj["sizeDecimation"].toInt(8));
// flip mode
_grabber.setFlipMode(obj["flip"].toString("no-change"));
// image cropping
_grabber.setCropping(
obj["cropLeft"].toInt(0),

View File

@@ -12,8 +12,8 @@ QtGrabber::QtGrabber(int cropLeft, int cropRight, int cropTop, int cropBottom, i
: Grabber("QTGRABBER", 0, 0, cropLeft, cropRight, cropTop, cropBottom)
, _display(unsigned(display))
, _pixelDecimation(pixelDecimation)
, _screenWidth(0)
, _screenHeight(0)
, _calculatedWidth(0)
, _calculatedHeight(0)
, _src_x(0)
, _src_y(0)
, _src_x_max(0)
@@ -98,20 +98,13 @@ int QtGrabber::grabFrame(Image<ColorRgb> & image)
setEnabled(setupDisplay());
return -1;
}
QPixmap originalPixmap = _screen->grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max);
QPixmap resizedPixmap = originalPixmap.scaled(_width,_height);
QImage imageFrame = resizedPixmap.toImage().convertToFormat( QImage::Format_RGB888);
image.resize(imageFrame.width(), imageFrame.height());
for (int y=0; y<imageFrame.height(); ++y)
for (int x=0; x<imageFrame.width(); ++x)
{
QColor inPixel(imageFrame.pixel(x,y));
ColorRgb & outPixel = image(x,y);
outPixel.red = inPixel.red();
outPixel.green = inPixel.green();
outPixel.blue = inPixel.blue();
}
QPixmap originalPixmap = _screen->grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max);
QImage imageFrame = originalPixmap.toImage().scaled(_calculatedWidth, _calculatedHeight).convertToFormat( QImage::Format_RGB888);
image.resize(_calculatedWidth, _calculatedHeight);
for (int y = 0; y < imageFrame.height(); y++)
memcpy((unsigned char*)image.memptr() + y * image.width() * 3, (unsigned char*)imageFrame.scanLine(y), imageFrame.width() * 3);
return 0;
}
@@ -122,59 +115,59 @@ int QtGrabber::updateScreenDimensions(bool force)
return -1;
const QRect& geo = _screen->geometry();
if (!force && _screenWidth == unsigned(geo.right()) && _screenHeight == unsigned(geo.bottom()))
if (!force && _width == unsigned(geo.right()) && _height == unsigned(geo.bottom()))
{
// No update required
return 0;
}
Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _screenWidth, _screenHeight, geo.right(), geo.bottom());
_screenWidth = geo.right() - geo.left();
_screenHeight = geo.bottom() - geo.top();
Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _width, _height, geo.right(), geo.bottom());
_width = geo.right() - geo.left();
_height = geo.bottom() - geo.top();
int width=0, height=0;
// Image scaling is performed by Qt
width = (_screenWidth > unsigned(_cropLeft + _cropRight))
? ((_screenWidth - _cropLeft - _cropRight) / _pixelDecimation)
: (_screenWidth / _pixelDecimation);
width = (_width > (_cropLeft + _cropRight))
? ((_width - _cropLeft - _cropRight) / _pixelDecimation)
: (_width / _pixelDecimation);
height = (_screenHeight > unsigned(_cropTop + _cropBottom))
? ((_screenHeight - _cropTop - _cropBottom) / _pixelDecimation)
: (_screenHeight / _pixelDecimation);
height = (_height > (_cropTop + _cropBottom))
? ((_height - _cropTop - _cropBottom) / _pixelDecimation)
: (_height / _pixelDecimation);
// calculate final image dimensions and adjust top/left cropping in 3D modes
switch (_videoMode)
{
case VideoMode::VIDEO_3DSBS:
_width = width /2;
_height = height;
_calculatedWidth = width /2;
_calculatedHeight = height;
_src_x = _cropLeft / 2;
_src_y = _cropTop;
_src_x_max = (_screenWidth / 2) - _cropRight;
_src_y_max = _screenHeight - _cropBottom;
_src_x_max = (_width / 2) - _cropRight - _cropLeft;
_src_y_max = _height - _cropBottom - _cropTop;
break;
case VideoMode::VIDEO_3DTAB:
_width = width;
_height = height / 2;
_calculatedWidth = width;
_calculatedHeight = height / 2;
_src_x = _cropLeft;
_src_y = _cropTop / 2;
_src_x_max = _screenWidth - _cropRight;
_src_y_max = (_screenHeight / 2) - _cropBottom;
_src_x_max = _width - _cropRight - _cropLeft;
_src_y_max = (_height / 2) - _cropBottom - _cropTop;
break;
case VideoMode::VIDEO_2D:
default:
_width = width;
_height = height;
_calculatedWidth = width;
_calculatedHeight = height;
_src_x = _cropLeft;
_src_y = _cropTop;
_src_x_max = _screenWidth - _cropRight;
_src_y_max = _screenHeight - _cropBottom;
_src_x_max = _width - _cropRight - _cropLeft;
_src_y_max = _height - _cropBottom - _cropTop;
break;
}
Info(_log, "Update output image resolution to [%dx%d]", _width, _height);
Info(_log, "Update output image resolution to [%dx%d]", _calculatedWidth, _calculatedHeight);
return 1;
}

View File

@@ -4,6 +4,7 @@ Grabber::Grabber(const QString& grabberName, int width, int height, int cropLeft
: _imageResampler()
, _useImageResampler(true)
, _videoMode(VideoMode::VIDEO_2D)
, _flipMode(FlipMode::NO_CHANGE)
, _width(width)
, _height(height)
, _fps(15)
@@ -35,6 +36,16 @@ void Grabber::setVideoMode(VideoMode mode)
}
}
void Grabber::setFlipMode(FlipMode mode)
{
Debug(_log,"Set flipmode to %d", mode);
_flipMode = mode;
if ( _useImageResampler )
{
_imageResampler.setFlipMode(_flipMode);
}
}
void Grabber::setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom)
{
if (_width>0 && _height>0)

View File

@@ -52,6 +52,18 @@
"required" : true,
"propertyOrder" : 7
},
"flip" :
{
"type" : "string",
"title" : "edt_conf_v4l2_flip_title",
"enum" : ["NO_CHANGE", "HORIZONTAL","VERTICAL","BOTH"],
"default" : "NO_CHANGE",
"options" : {
"enum_titles" : ["edt_conf_enum_NO_CHANGE", "edt_conf_enum_HORIZONTAL", "edt_conf_enum_VERTICAL", "edt_conf_enum_BOTH"]
},
"required" : true,
"propertyOrder" : 8
},
"width" :
{
"type" : "integer",
@@ -63,8 +75,8 @@
"hidden":true
},
"required" : true,
"propertyOrder" : 9,
"comment" : "The 'resolutions' settings are dynamically inserted into the WebUI under PropertyOrder '8'."
"propertyOrder" : 10,
"comment" : "The 'resolutions' settings are dynamically inserted into the WebUI under PropertyOrder '9'."
},
"height" :
{
@@ -77,8 +89,8 @@
"hidden":true
},
"required" : true,
"propertyOrder" : 10,
"comment" : "The 'resolutions' settings are dynamically inserted into the WebUI under PropertyOrder '6'."
"propertyOrder" : 11,
"comment" : "The 'resolutions' settings are dynamically inserted into the WebUI under PropertyOrder '9'."
},
"fps" :
{
@@ -91,8 +103,8 @@
"hidden":true
},
"required" : true,
"propertyOrder" : 12,
"comment" : "The 'framerates' setting is dynamically inserted into the WebUI under PropertyOrder '11'."
"propertyOrder" : 13,
"comment" : "The 'framerates' setting is dynamically inserted into the WebUI under PropertyOrder '12'."
},
"fpsSoftwareDecimation" :
{
@@ -102,7 +114,7 @@
"maximum" : 60,
"default" : 1,
"required" : true,
"propertyOrder" : 13
"propertyOrder" : 14
},
"sizeDecimation" :
{
@@ -112,7 +124,7 @@
"maximum" : 30,
"default" : 6,
"required" : true,
"propertyOrder" : 14
"propertyOrder" : 15
},
"cropLeft" :
{
@@ -122,7 +134,7 @@
"default" : 0,
"append" : "edt_append_pixel",
"required" : true,
"propertyOrder" : 15
"propertyOrder" : 16
},
"cropRight" :
{
@@ -132,7 +144,7 @@
"default" : 0,
"append" : "edt_append_pixel",
"required" : true,
"propertyOrder" : 16
"propertyOrder" : 17
},
"cropTop" :
{
@@ -142,7 +154,7 @@
"default" : 0,
"append" : "edt_append_pixel",
"required" : true,
"propertyOrder" : 17
"propertyOrder" : 18
},
"cropBottom" :
{
@@ -152,7 +164,7 @@
"default" : 0,
"append" : "edt_append_pixel",
"required" : true,
"propertyOrder" : 18
"propertyOrder" : 19
},
"cecDetection" :
{
@@ -160,7 +172,7 @@
"title" : "edt_conf_v4l2_cecDetection_title",
"default" : false,
"required" : true,
"propertyOrder" : 19
"propertyOrder" : 20
},
"signalDetection" :
{
@@ -168,7 +180,7 @@
"title" : "edt_conf_v4l2_signalDetection_title",
"default" : false,
"required" : true,
"propertyOrder" : 20
"propertyOrder" : 21
},
"redSignalThreshold" :
{
@@ -184,7 +196,7 @@
}
},
"required" : true,
"propertyOrder" : 21
"propertyOrder" : 22
},
"greenSignalThreshold" :
{
@@ -200,7 +212,7 @@
}
},
"required" : true,
"propertyOrder" : 22
"propertyOrder" : 23
},
"blueSignalThreshold" :
{
@@ -216,7 +228,7 @@
}
},
"required" : true,
"propertyOrder" : 23
"propertyOrder" : 24
},
"noSignalCounterThreshold" :
{
@@ -231,7 +243,7 @@
}
},
"required" : true,
"propertyOrder" : 24
"propertyOrder" : 25
},
"sDVOffsetMin" :
{
@@ -247,7 +259,7 @@
}
},
"required" : true,
"propertyOrder" : 25
"propertyOrder" : 26
},
"sDVOffsetMax" :
{
@@ -263,7 +275,7 @@
}
},
"required" : true,
"propertyOrder" : 26
"propertyOrder" : 27
},
"sDHOffsetMin" :
{
@@ -279,7 +291,7 @@
}
},
"required" : true,
"propertyOrder" : 27
"propertyOrder" : 28
},
"sDHOffsetMax" :
{
@@ -295,7 +307,7 @@
}
},
"required" : true,
"propertyOrder" : 28
"propertyOrder" : 29
},
"hardware_brightness" :
{
@@ -303,7 +315,7 @@
"title" : "edt_conf_v4l2_hardware_brightness_title",
"default" : 0,
"required" : true,
"propertyOrder" : 29
"propertyOrder" : 30
},
"hardware_contrast" :
{
@@ -311,7 +323,7 @@
"title" : "edt_conf_v4l2_hardware_contrast_title",
"default" : 0,
"required" : true,
"propertyOrder" : 30
"propertyOrder" : 31
},
"hardware_saturation" :
{
@@ -319,7 +331,7 @@
"title" : "edt_conf_v4l2_hardware_saturation_title",
"default" : 0,
"required" : true,
"propertyOrder" : 31
"propertyOrder" : 32
},
"hardware_hue" :
{
@@ -327,7 +339,7 @@
"title" : "edt_conf_v4l2_hardware_hue_title",
"default" : 0,
"required" : true,
"propertyOrder" : 32
"propertyOrder" : 33
}
},
"additionalProperties" : true