Extract ImageResampler from X11Grabber

Former-commit-id: 63e95fa85fc0ef5a66f6eb8b396cf4b6370cf5dd
This commit is contained in:
poljvd 2014-12-14 14:26:59 +01:00
parent 93de656985
commit e608b11caa
16 changed files with 517 additions and 345 deletions

View File

@ -57,8 +57,8 @@ include_directories(${CMAKE_SOURCE_DIR}/include)
# Prefer static linking over dynamic
#set(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.so")
#set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_BUILD_TYPE "Release")
set(CMAKE_BUILD_TYPE "Debug")
#set(CMAKE_BUILD_TYPE "Release")
# enable C++11
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall")

View File

@ -11,11 +11,11 @@
// util includes
#include <utils/Image.h>
#include <utils/ColorRgb.h>
#include <utils/PixelFormat.h>
#include <utils/VideoMode.h>
// grabber includes
#include <grabber/VideoStandard.h>
#include <grabber/PixelFormat.h>
/// Capture class for V4L2 devices
///

View File

@ -2,6 +2,7 @@
// Hyperion-utils includes
#include <utils/Image.h>
#include <utils/ColorRgb.h>
#include <utils/ImageResampler.h>
// X11 includes
#include <X11/Xlib.h>
@ -10,7 +11,7 @@ class X11Grabber
{
public:
X11Grabber(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation);
X11Grabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation);
virtual ~X11Grabber();
@ -19,11 +20,12 @@ public:
Image<ColorRgb> & grab();
private:
ImageResampler _imageResampler;
const unsigned _pixelDecimation;
const unsigned _cropWidth;
const unsigned _cropHeight;
int _cropLeft;
int _cropRight;
int _cropTop;
int _cropBottom;
/// Reference to the X11 display (nullptr if not opened)
Display * _x11Display;

View File

@ -96,11 +96,6 @@ public:
return _height;
}
uint8_t alpha(const unsigned pixel) const
{
return (_pixels + pixel)->red;
}
uint8_t red(const unsigned pixel) const
{
return (_pixels + pixel)->red;

View File

@ -0,0 +1,40 @@
#pragma once
#include <utils/VideoMode.h>
#include <utils/PixelFormat.h>
#include <utils/Image.h>
#include <utils/ColorRgb.h>
class ImageResampler
{
public:
ImageResampler();
~ImageResampler();
void setHorizontalPixelDecimation(int decimator);
void setVerticalPixelDecimation(int decimator);
void setCropping(int cropLeft,
int cropRight,
int cropTop,
int cropBottom);
void set3D(VideoMode mode);
void processImage(const uint8_t * data, int width, int height, int lineLength, PixelFormat pixelFormat,
Image<ColorRgb> & outputImage) const;
private:
static inline uint8_t clamp(int x);
static void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t & r, uint8_t & g, uint8_t & b);
private:
int _horizontalDecimation;
int _verticalDecimation;
int _cropLeft;
int _cropRight;
int _cropTop;
int _cropBottom;
VideoMode _videoMode;
};

View File

@ -10,6 +10,7 @@ enum PixelFormat {
PIXELFORMAT_YUYV,
PIXELFORMAT_UYVY,
PIXELFORMAT_RGB32,
PIXELFORMAT_BGR32,
PIXELFORMAT_NO_CHANGE
};
@ -30,6 +31,10 @@ inline PixelFormat parsePixelFormat(std::string pixelFormat)
{
return PIXELFORMAT_RGB32;
}
else if (pixelFormat == "bgr32")
{
return PIXELFORMAT_BGR32;
}
// return the default NO_CHANGE
return PIXELFORMAT_NO_CHANGE;

View File

@ -9,7 +9,6 @@ SET(V4L2_QT_HEADERS
SET(V4L2_HEADERS
${CURRENT_HEADER_DIR}/VideoStandard.h
${CURRENT_HEADER_DIR}/PixelFormat.h
)
SET(V4L2_SOURCES

View File

@ -8,16 +8,20 @@
// X11Grabber includes
#include <grabber/X11Grabber.h>
X11Grabber::X11Grabber(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation) :
_pixelDecimation(pixelDecimation),
_cropWidth(cropHorizontal),
_cropHeight(cropVertical),
X11Grabber::X11Grabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation) :
_imageResampler(),
_cropLeft(cropLeft),
_cropRight(cropRight),
_cropTop(cropTop),
_cropBottom(cropBottom),
_x11Display(nullptr),
_screenWidth(0),
_screenHeight(0),
_image(0,0)
{
// empty
_imageResampler.setHorizontalPixelDecimation(horizontalPixelDecimation);
_imageResampler.setVerticalPixelDecimation(verticalPixelDecimation);
_imageResampler.setCropping(0, 0, 0, 0); // cropping is performed by XGetImage
}
X11Grabber::~X11Grabber()
@ -51,42 +55,18 @@ Image<ColorRgb> & X11Grabber::grab()
updateScreenDimensions();
const int croppedWidth = _screenWidth - 2*_cropWidth;
const int croppedHeight = _screenHeight - 2*_cropHeight;
const unsigned croppedWidth = _screenWidth - _cropLeft - _cropRight;
const unsigned croppedHeight = _screenHeight - _cropTop - _cropBottom;
// Capture the current screen
XImage * xImage = XGetImage(_x11Display, DefaultRootWindow(_x11Display), _cropWidth, _cropHeight, croppedWidth, croppedHeight, AllPlanes, ZPixmap);
XImage * xImage = XGetImage(_x11Display, DefaultRootWindow(_x11Display), _cropLeft, _cropTop, croppedWidth, croppedHeight, AllPlanes, ZPixmap);
if (xImage == nullptr)
{
std::cerr << "Grab failed" << std::endl;
return _image;
}
// Copy the capture XImage to the local image (and apply required decimation)
ColorRgb * outputPtr = _image.memptr();
int width = 0;
int height = 0;
for (int iY=(_pixelDecimation/2); iY<croppedHeight; iY+=_pixelDecimation)
{
width = 0;
for (int iX=(_pixelDecimation/2); iX<croppedWidth; iX+=_pixelDecimation)
{
// Extract the pixel from the X11-image
const uint32_t pixel = uint32_t(XGetPixel(xImage, iX, iY));
// Assign the color value
outputPtr->red = uint8_t((pixel >> 16) & 0xff);
outputPtr->green = uint8_t((pixel >> 8) & 0xff);
outputPtr->blue = uint8_t((pixel >> 0) & 0xff);
// Move to the next output pixel
++outputPtr;
++width;
}
++height;
}
std::cout << "decimated X11 message: " << width << " x " << height << std::endl;
_imageResampler.processImage(reinterpret_cast<const uint8_t *>(xImage->data), xImage->width, xImage->height, xImage->bytes_per_line, PIXELFORMAT_BGR32, _image);
// Cleanup allocated resources of the X11 grab
XDestroyImage(xImage);
@ -114,11 +94,5 @@ int X11Grabber::updateScreenDimensions()
_screenHeight = window_attributes_return.height;
std::cout << "[" << _screenWidth << "x" << _screenHeight <<"]" << std::endl;
// Update the size of the buffer used to transfer the screenshot
int width = (_screenWidth - 2 * _cropWidth - _pixelDecimation/2 - 1) / _pixelDecimation + 1;
int height = (_screenHeight - 2 * _cropHeight - _pixelDecimation/2 - 1) / _pixelDecimation + 1;
_image.resize(width, height);
return 0;
}

View File

@ -13,6 +13,12 @@ add_library(hyperion-utils
${CURRENT_HEADER_DIR}/Image.h
${CURRENT_HEADER_DIR}/Sleep.h
${CURRENT_HEADER_DIR}/PixelFormat.h
${CURRENT_HEADER_DIR}/VideoMode.h
${CURRENT_HEADER_DIR}/ImageResampler.h
${CURRENT_SOURCE_DIR}/ImageResampler.cpp
${CURRENT_HEADER_DIR}/HsvTransform.h
${CURRENT_SOURCE_DIR}/HsvTransform.cpp
${CURRENT_HEADER_DIR}/RgbChannelTransform.h

View File

@ -0,0 +1,133 @@
#include "utils/ImageResampler.h"
ImageResampler::ImageResampler() :
_horizontalDecimation(1),
_verticalDecimation(1),
_cropLeft(0),
_cropRight(0),
_cropTop(0),
_cropBottom(0),
_videoMode(VIDEO_2D)
{
}
ImageResampler::~ImageResampler()
{
}
void ImageResampler::setHorizontalPixelDecimation(int decimator)
{
_horizontalDecimation = decimator;
}
void ImageResampler::setVerticalPixelDecimation(int decimator)
{
_verticalDecimation = decimator;
}
void ImageResampler::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom)
{
_cropLeft = cropLeft;
_cropRight = cropRight;
_cropTop = cropTop;
_cropBottom = cropBottom;
}
void ImageResampler::set3D(VideoMode mode)
{
_videoMode = mode;
}
void ImageResampler::processImage(const uint8_t * data, int width, int height, int lineLength, PixelFormat pixelFormat, Image<ColorRgb> &outputImage) const
{
int cropLeft = _cropLeft;
int cropRight = _cropRight;
int cropTop = _cropTop;
int cropBottom = _cropBottom;
// handle 3D mode
switch (_videoMode)
{
case VIDEO_3DSBS:
cropRight = width/2;
break;
case VIDEO_3DTAB:
cropBottom = height/2;
break;
default:
break;
}
// calculate the output size
int outputWidth = (width - cropLeft - cropRight - _horizontalDecimation/2 + _horizontalDecimation - 1) / _horizontalDecimation;
int outputHeight = (height - cropTop - cropBottom - _verticalDecimation/2 + _verticalDecimation - 1) / _verticalDecimation;
outputImage.resize(outputWidth, outputHeight);
for (int yDest = 0, ySource = cropTop + _verticalDecimation/2; yDest < outputHeight; ySource += _verticalDecimation, ++yDest)
{
for (int xDest = 0, xSource = cropLeft + _horizontalDecimation/2; xDest < outputWidth; xSource += _horizontalDecimation, ++xDest)
{
ColorRgb & rgb = outputImage(xDest, yDest);
switch (pixelFormat)
{
case PIXELFORMAT_UYVY:
{
int index = lineLength * ySource + xSource * 2;
uint8_t y = data[index+1];
uint8_t u = ((xSource&1) == 0) ? data[index ] : data[index-2];
uint8_t v = ((xSource&1) == 0) ? data[index+2] : data[index ];
yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue);
}
break;
case PIXELFORMAT_YUYV:
{
int index = lineLength * ySource + xSource * 2;
uint8_t y = data[index];
uint8_t u = ((xSource&1) == 0) ? data[index+1] : data[index-1];
uint8_t v = ((xSource&1) == 0) ? data[index+3] : data[index+1];
yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue);
}
break;
case PIXELFORMAT_RGB32:
{
int index = lineLength * ySource + xSource * 4;
rgb.red = data[index ];
rgb.green = data[index+1];
rgb.blue = data[index+2];
}
break;
case PIXELFORMAT_BGR32:
{
int index = lineLength * ySource + xSource * 4;
rgb.blue = data[index ];
rgb.green = data[index+1];
rgb.red = data[index+2];
}
break;
case PIXELFORMAT_NO_CHANGE:
std::cerr << "Invalid pixel format given" << std::endl;
break;
}
}
}
}
uint8_t ImageResampler::clamp(int x)
{
return (x<0) ? 0 : ((x>255) ? 255 : uint8_t(x));
}
void ImageResampler::yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t &r, uint8_t &g, uint8_t &b)
{
// see: http://en.wikipedia.org/wiki/YUV#Y.27UV444_to_RGB888_conversion
int c = y - 16;
int d = u - 128;
int e = v - 128;
r = clamp((298 * c + 409 * e + 128) >> 8);
g = clamp((298 * c - 100 * d - 208 * e + 128) >> 8);
b = clamp((298 * c + 516 * d + 128) >> 8);
}

View File

@ -2,7 +2,7 @@
#include <getoptPlusPlus/getoptpp.h>
// grabber includes
#include <grabber/PixelFormat.h>
#include <utils/PixelFormat.h>
using namespace vlofgren;

View File

@ -102,6 +102,7 @@ int main(int argc, char** argv)
return 0;
}
// cropping values if not defined
if (!argCropLeft.isSet()) argCropLeft.setDefault(argCropWidth.getValue());
if (!argCropRight.isSet()) argCropRight.setDefault(argCropWidth.getValue());
if (!argCropTop.isSet()) argCropTop.setDefault(argCropHeight.getValue());

View File

@ -2,9 +2,9 @@
// Hyperion-X11 includes
#include "X11Wrapper.h"
X11Wrapper::X11Wrapper(int grabInterval, const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation) :
X11Wrapper::X11Wrapper(int grabInterval, int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation) :
_timer(this),
_grabber(cropHorizontal, cropVertical, pixelDecimation)
_grabber(cropLeft, cropRight, cropTop, cropBottom, horizontalPixelDecimation, verticalPixelDecimation)
{
_timer.setSingleShot(false);
_timer.setInterval(grabInterval);

View File

@ -9,7 +9,7 @@ class X11Wrapper : public QObject
{
Q_OBJECT
public:
X11Wrapper(int grabInterval, const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation);
X11Wrapper(int grabInterval, int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation);
const Image<ColorRgb> & getScreenshot();

View File

@ -30,9 +30,13 @@ int main(int argc, char ** argv)
ParameterSet & parameters = optionParser.getParameters();
IntParameter & argFps = parameters.add<IntParameter> ('f', "framerate", "Cpture frame rate [default=10]");
IntParameter & argCropWidth = parameters.add<IntParameter> (0x0, "crop-width", "Number of pixels to crop from the left and right sides in the picture before decimation [default=0]");
IntParameter & argCropHeight = parameters.add<IntParameter> (0x0, "crop-height", "Number of pixels to crop from the top and the bottom in the picture before decimation [default=0]");
IntParameter & argSizeDecimation = parameters.add<IntParameter> ('s', "size-decimator", "Decimation factor for the output size [default=16]");
IntParameter & argCropWidth = parameters.add<IntParameter> (0x0, "crop-width", "Number of pixels to crop from the left and right sides of the picture before decimation [default=0]");
IntParameter & argCropHeight = parameters.add<IntParameter> (0x0, "crop-height", "Number of pixels to crop from the top and the bottom of the picture before decimation [default=0]");
IntParameter & argCropLeft = parameters.add<IntParameter> (0x0, "crop-left", "Number of pixels to crop from the left of the picture before decimation (overrides --crop-width)");
IntParameter & argCropRight = parameters.add<IntParameter> (0x0, "crop-right", "Number of pixels to crop from the right of the picture before decimation (overrides --crop-width)");
IntParameter & argCropTop = parameters.add<IntParameter> (0x0, "crop-top", "Number of pixels to crop from the top of the picture before decimation (overrides --crop-height)");
IntParameter & argCropBottom = parameters.add<IntParameter> (0x0, "crop-bottom", "Number of pixels to crop from the bottom of the picture before decimation (overrides --crop-height)");
IntParameter & argSizeDecimation = parameters.add<IntParameter> ('s', "size-decimator", "Decimation factor for the output size [default=8]");
SwitchParameter<> & argScreenshot = parameters.add<SwitchParameter<>> (0x0, "screenshot", "Take a single screenshot, save it to file and quit");
StringParameter & argAddress = parameters.add<StringParameter> ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]");
IntParameter & argPriority = parameters.add<IntParameter> ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]");
@ -43,7 +47,7 @@ int main(int argc, char ** argv)
argFps.setDefault(10);
argCropWidth.setDefault(0);
argCropHeight.setDefault(0);
argSizeDecimation.setDefault(16);
argSizeDecimation.setDefault(8);
argAddress.setDefault("127.0.0.1:19445");
argPriority.setDefault(800);
@ -57,9 +61,22 @@ int main(int argc, char ** argv)
return 0;
}
// cropping values if not defined
if (!argCropLeft.isSet()) argCropLeft.setDefault(argCropWidth.getValue());
if (!argCropRight.isSet()) argCropRight.setDefault(argCropWidth.getValue());
if (!argCropTop.isSet()) argCropTop.setDefault(argCropHeight.getValue());
if (!argCropBottom.isSet()) argCropBottom.setDefault(argCropHeight.getValue());
// Create the X11 grabbing stuff
int grabInterval = 1000 / argFps.getValue();
X11Wrapper x11Wrapper(grabInterval, argCropWidth.getValue(), argCropHeight.getValue(), argSizeDecimation.getValue());
X11Wrapper x11Wrapper(
grabInterval,
argCropLeft.getValue(),
argCropRight.getValue(),
argCropTop.getValue(),
argCropBottom.getValue(),
argSizeDecimation.getValue(), // horizontal decimation
argSizeDecimation.getValue()); // vertical decimation
if (argScreenshot.isSet())
{