Creating ARGB QImage from uint32 or uchar array - c++

When I try to create a ARGB32 QImage from a reinterpret_cast<uchar*>(quint32*) using the QImage constructor the Image looses its color and alpha channel and the resulting QImage is grayscale!
The grayscale image is displayed as expected, if I was trying to display it in grayscale. So I know the scaling and indexing of ushort data to the quint32 array went well, but what is going wrong?
A Qt forum post suggested to do it the way I am doing it (as far as I can see), but maybe behavior has changed since that version of Qt? (I am Using Qt 5.9)
I realise that the documentation says:
data must be 32-bit aligned, and each scanline of data in the image
must also be 32-bit aligned.
But I would expect quint32 to be 32-bit aligned even after reinterpret_cast<uchar*>()?
Now the details:
I am converting the results of a calculation (an array with unsigned short values) to a semi-transparent blue-to-green-to-red image like this:
inline uchar val_to_blue(const double val) {
if (val > 0.5)
return 0;
else if (val < 0.25)
return 255;
else // x={.5,...,.25}:a=255/(.25-.5)=-4*255 & b=-255*0.5/(0.25-0.5)=4/2*255=2*255
return (uchar)(val * -4.0 * 255.0) + 2 * 255;
}
inline uchar val_to_green(const double val) {
if (val > 0.25 && val < 0.75)
return 255;
else if (val < 0.25)// x={0,...,.25}:a=255/(.25-0)=4*255 & b=-255*0/(0.25-0)=0
return (uchar)(val * 4.0 * 255.0);
else // if (val > .75) // x={.75,...,1}:a=255/(.75-.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * -4.0 * 255.0) - 2 * 255;
}
inline uchar val_to_red(const double val) {
if (val < 0.5)
return 0;
if (val > 0.75)
return 255;
else // x={0.5,...,0.75}:a=255/(0.75-0.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * 4.0 * 255.0) - 2 * 255;
}
inline QRgb val_to_rgba_scale(const double val) {
return qRgba( // ax+b={0,...,255} for x={i,...,j}, a=255/(j-i), b= -255i/(j-i)
val_to_blue(val),
val_to_green(val),
val_to_red(val),
(uchar)(val * 81)
);
}
Where val is a double between 0 and 1 scaled from the ushort data.
Each QRgb value is stored at the corresponding index of a quint32 array, like this:
if (m_pData[i*m_iWidth + j] >= uppVal)
tmpData[tmpIdx] = 0x45ff0000;
else if (m_pData[i*m_iWidth + j] <= lowVal)
tmpData[tmpIdx] = 0x00000000;
else
tmpData[tmpIdx] = val_to_rgba_scale((m_pData[i*m_iWidth + j] - lowVal) / (double)winWidth);
Where (m_pData[i*m_iWidth + j] - lowVal) / (double)winWidthis the ushort-to-double scaling method.
This is done in a for loop.
Finally I attempt to construct the image with:
QImage tmpQImage = QImage(reinterpret_cast<unsigned char*>(tmpData), m_iWidth, m_iHeight, QImage::Format_ARGB32);
But this doesn't work as I expect, because tmpQImage.allGray() returns true when called immediately after!
What am I doing wrong, and what should I do instead to create a ARGB image and keep both the colors and alpha channel?

I tried to reproduce your problem but I couldn't.
Either the actual issue of the OP is not part of the presented code, or I accidentally missed a detail when I tried to form an MCVE from the OP.
However, I want to present what I got so far as this may be helpful to fix the OP.
My source testQImageGrayToRGB.cc:
#include <vector>
#include <QtWidgets>
typedef unsigned char uchar;
namespace AGA {
uchar val_to_blue(const double val) {
if (val > 0.5)
return 0;
else if (val < 0.25)
return 255;
else // x={.5,...,.25}:a=255/(.25-.5)=-4*255 & b=-255*0.5/(0.25-0.5)=4/2*255=2*255
return (uchar)(val * -4.0 * 255.0) + 2 * 255;
}
uchar val_to_green(const double val) {
if (val > 0.25 && val < 0.75)
return 255;
else if (val < 0.25)// x={0,...,.25}:a=255/(.25-0)=4*255 & b=-255*0/(0.25-0)=0
return (uchar)(val * 4.0 * 255.0);
else // if (val > .75) // x={.75,...,1}:a=255/(.75-.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * -4.0 * 255.0) - 2 * 255;
}
uchar val_to_red(const double val) {
if (val < 0.5)
return 0;
if (val > 0.75)
return 255;
else // x={0.5,...,0.75}:a=255/(0.75-0.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * 4.0 * 255.0) - 2 * 255;
}
} // namespace AGA
namespace DS {
uchar val_to_blue(const double val)
{
return val < 0.25 ? 255
: val < 0.5 ? (0.5 - val) * 4 * 255
: 0;
}
uchar val_to_green(const double val)
{
return val < 0.25 ? val * 4 * 255
: val < 0.75 ? 255
: (1.0 - val) * 4 * 255;
}
uchar val_to_red(const double val)
{
return val < 0.5 ? 0
: val < 0.75 ? (val - 0.5) * 4 * 255
: 255;
}
} // namespace DS
std::vector<quint32> buildImageData(
const int w, const int h,
uchar (*pFuncValToR)(double),
uchar (*pFuncValToG)(double),
uchar (*pFuncValToB)(double))
{
// make temp. buffer to build up raw image data
std::vector<quint32> data(w * h);
// fill raw image - make values 0 ... 1 in n steps
const int n = w - 1;
for (int x = 0; x < w; ++x) {
const double v = (double)x / n;
QRgb qRgb = qRgba(pFuncValToR(v), pFuncValToG(v), pFuncValToB(v), 255);
for (int y = 0; y < h; ++y) data[y * w + x] = qRgb;
}
// done
return data;
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version: " << QT_VERSION_STR;
QApplication app(argc, argv);
// build contents
enum { w = 256, h = 32 };
std::vector<quint32> dataAGA = buildImageData(w, h,
&AGA::val_to_red, &AGA::val_to_green, &AGA::val_to_blue);
QImage qImgAGA((const uchar*)dataAGA.data(), w, h, QImage::Format_ARGB32);
std::vector<quint32> dataDS = buildImageData(w, h,
&DS::val_to_red, &DS::val_to_green, &DS::val_to_blue);
QImage qImgDS((const uchar*)dataDS.data(), w, h, QImage::Format_ARGB32);
// build some GUI
QWidget win;
QVBoxLayout qVBox;
QLabel qLblAGA(
QString::fromUtf8("QImage (Functions of Andreas Gravgaard Andersen):"));
qVBox.addWidget(&qLblAGA);
QLabel qLblImgAGA;
qLblImgAGA.setPixmap(QPixmap::fromImage(qImgAGA));
qVBox.addWidget(&qLblImgAGA);
QLabel qLblDS(
QString::fromUtf8("QImage (Functions of Scheff):"));
qVBox.addWidget(&qLblDS);
QLabel qLblImgDS;
qLblImgDS.setPixmap(QPixmap::fromImage(qImgDS));
qVBox.addWidget(&qLblImgDS);
win.setLayout(&qVBox);
win.show();
// exec. application
return app.exec();
}
I compiled and tested it with VS2013, Qt5.6 on Windows 10 (64 bit):
Notes:
The val_to_ functions made me a little bit suspicious: an expression casted to (uchar), then a constant term added (which definitely does not fit into (uchar), the result returned as uchar...
Hmm...
Therefore, I remade them – with a little bit clean-up.
Actually, the visual comparison shows the differences are nearly invisible (with the only exception of the red line in the yellow region).
I had no problems to make a QImage out of the raw quint32 array (including the cast-to-uchar*-hack).
Update:
May be, it is not obvious: The sample code is carefully designed to grant that life-time of buffer data (std::vector<quint32> dataAGA and std::vector<quint32> dataDS) is longer than the life-time of Qt images (QImage qImgAGA and QImage qImgDS). This has been done according to the Qt doc. for QImage::QImage():
The buffer must remain valid throughout the life of the QImage and all copies that have not been modified or otherwise detached from the original buffer. The image does not delete the buffer at destruction. You can provide a function pointer cleanupFunction along with an extra pointer cleanupInfo that will be called when the last copy is destroyed.
Image data may consume a significant amount of memory. Thus, the QImage implementation tries to prevent unnecessary copies (to safe memory space and time). Instead, the "user" (i.e. application developer) is responsible to ensure proper storage of image data.

Related

What is the highest bit depth greyscale image I can export from FreeImage?

As context, I'm working with building a topographic program which needs relatively extreme detail. I do not expect the files to be small, and they do not formally need to be viewed on a monitor, they just need to have very high resolution.
I know that most image formats are limited to 8 bpp, on account of the standard limits on both monitors (at a reasonable price) and on human perception. However, 2⁸ is just 256 possible values, which induces plateauing artifacts in a reconstructed displacement. 2¹⁶ may be close enough at 65,536 possible values, which I have achieved.
I'm using FreeImage and DLang to construct the data, currently on a Linux Mint machine.
However, when I went on to 2³², software support seemed to fade on me. I tried a TIFF of this form and nothing seemed to be able to interpret it, either showing a completely (or mostly) transparent image (remembering that I didn't expect any monitor to really support 2³² shades of a channel) or complaining about being unable to decode the RGB data. I imagine that it's because it was assumed to be an RGB or RGBA image.
FreeImage is reasonably well documented for most purposes, but I'm now wondering, what is the highest-precision single-channel format I can export, and how would I do it? Can anyone provide an example? Am I really limited, in any typical and not-home-rolled image format, to 16-bit? I know that's high enough for, say, medical imaging, but I'm sure I'm not the first person to try to aim higher and we science-types can be pretty ambitious about our precision-level…
Did I make a glaring mistake in my code? Is there something else I should try instead for this kind of precision?
Here's my code.
The 16-bit TIFF that worked
void writeGrayscaleMonochromeBitmap(const double width, const double height) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_UINT16, cast(int)width, cast(int)height);
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
for(int x = 0; x < width; x++) {
ushort v = cast(ushort)((x * 0xFFFF)/width);
ubyte[2] bytes = nativeToLittleEndian(cast(ushort)(x/width * 0xFFFF));
scanline[x * ushort.sizeof + 0] = bytes[0];
scanline[x * ushort.sizeof + 1] = bytes[1];
}
}
FreeImage_Save(FIF_TIFF, bitmap, "test.tif", TIFF_DEFAULT);
FreeImage_Unload(bitmap);
}
The 32-bit TIFF that didn't really work
void writeGrayscaleMonochromeBitmap32(const double width, const double height) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_UINT32, cast(int)width, cast(int)height);
writeln(width, ", ", height);
writeln("Width: ", FreeImage_GetWidth(bitmap));
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
writeln(y, ": ", scanline);
for(int x = 0; x < width; x++) {
//writeln(x, " < ", width);
uint v = cast(uint)((x/width) * 0xFFFFFFFF);
writeln("V: ", v);
ubyte[4] bytes = nativeToLittleEndian(v);
scanline[x * uint.sizeof + 0] = bytes[0];
scanline[x * uint.sizeof + 1] = bytes[1];
scanline[x * uint.sizeof + 2] = bytes[2];
scanline[x * uint.sizeof + 3] = bytes[3];
}
}
FreeImage_Save(FIF_TIFF, bitmap, "test32.tif", TIFF_NONE);
FreeImage_Unload(bitmap);
}
Thanks for any pointers.
For a single channel, the highest available from FreeImage is 32-bit, as FIT_UINT32. However, the file format must be capable of this, and as of the moment, only TIFF appears to be up to the task (See page 104 of the Stanford Documentation). Additionally, most monitors are incapable of representing more than 8-bits-per-sample, 12 in extreme cases, so it is very difficult to read data back out and have it render properly.
A unit test involving comparing bytes before marshaling to the bitmap, and sampled from the same bitmap afterward, show that the data is in fact being encoded.
To imprint data to a 16-bit gray scale (currently supported by J2K, JP2, PGM, PGMRAW, PNG and TIF), you would do something like this:
void toFreeImageUINT16PNG(string fileName, const double width, const double height, double[] data) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_UINT16, cast(int)width, cast(int)height);
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
for(int x = 0; x < width; x++) {
//This magic has to happen with the y-coordinate in order to keep FreeImage from following its default behavior, and generating
//the image upside down.
ushort v = cast(ushort)(data[cast(ulong)(((height - 1) - y) * width + x)] * 0xFFFF); //((x * 0xFFFF)/width);
ubyte[2] bytes = nativeToLittleEndian(v);
scanline[x * ushort.sizeof + 0] = bytes[0];
scanline[x * ushort.sizeof + 1] = bytes[1];
}
}
FreeImage_Save(FIF_PNG, bitmap, fileName.toStringz);
FreeImage_Unload(bitmap);
}
Of course you would want to make adjustments for your target file type. To export as 48-bit RGB16, you would do this.
void toFreeImageColorPNG(string fileName, const double width, const double height, double[] data) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_RGB16, cast(int)width, cast(int)height);
uint pitch = FreeImage_GetPitch(bitmap);
uint bpp = FreeImage_GetBPP(bitmap);
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
for(int x = 0; x < width; x++) {
ulong offset = cast(ulong)((((height - 1) - y) * width + x) * 3);
ushort r = cast(ushort)(data[(offset + 0)] * 0xFFFF);
ushort g = cast(ushort)(data[(offset + 1)] * 0xFFFF);
ushort b = cast(ushort)(data[(offset + 2)] * 0xFFFF);
ubyte[6] bytes = nativeToLittleEndian(r) ~ nativeToLittleEndian(g) ~ nativeToLittleEndian(b);
scanline[(x * 3 * ushort.sizeof) + 0] = bytes[0];
scanline[(x * 3 * ushort.sizeof) + 1] = bytes[1];
scanline[(x * 3 * ushort.sizeof) + 2] = bytes[2];
scanline[(x * 3 * ushort.sizeof) + 3] = bytes[3];
scanline[(x * 3 * ushort.sizeof) + 4] = bytes[4];
scanline[(x * 3 * ushort.sizeof) + 5] = bytes[5];
}
}
FreeImage_Save(FIF_PNG, bitmap, fileName.toStringz);
FreeImage_Unload(bitmap);
}
Lastly, to encode a UINT32 greyscale image (limited purely to TIFF at the moment), you would do this.
void toFreeImageTIF32(string fileName, const double width, const double height, double[] data) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_UINT32, cast(int)width, cast(int)height);
//DEBUG
int xtest = cast(int)(width/2);
int ytest = cast(int)(height/2);
uint comp1a = cast(uint)(data[cast(ulong)(((height - 1) - ytest) * width + xtest)] * 0xFFFFFFFF);
writeln("initial: ", nativeToLittleEndian(comp1a));
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
for(int x = 0; x < width; x++) {
//This magic has to happen with the y-coordinate in order to keep FreeImage from following its default behavior, and generating
//the image upside down.
ulong i = cast(ulong)(((height - 1) - y) * width + x);
uint v = cast(uint)(data[i] * 0xFFFFFFFF);
ubyte[4] bytes = nativeToLittleEndian(v);
scanline[x * uint.sizeof + 0] = bytes[0];
scanline[x * uint.sizeof + 1] = bytes[1];
scanline[x * uint.sizeof + 2] = bytes[2];
scanline[x * uint.sizeof + 3] = bytes[3];
}
}
//DEBUG
ulong index = cast(ulong)(xtest * uint.sizeof);
writeln("Final: ", FreeImage_GetScanLine(bitmap, ytest)
[index .. index + uint.sizeof]);
FreeImage_Save(FIF_TIFF, bitmap, fileName.toStringz);
FreeImage_Unload(bitmap);
}
I've yet to find a program, built by anyone else, which will readily render a 32-bit gray-scale image on a monitor's available palette. However, I left my checking code in which will consistently write out the same array both at the top DEBUG and the bottom one, and that's consistent enough for me.
Hopefully this will help someone else out in the future.

YUV420p to RGB conversion has shifted U and V values - iOS Unity3D

I have a native plugin in Unity that decodes H264 frames to YUV420p using FFMPEG.
To display the output image, I rearrange the YUV values into an RGBA texture and convert YUV to RGB using Unity shader (just to make it faster).
The following is the rearrangement code in my native plugin:
unsigned char* yStartLocation = (unsigned char*)m_pFrame->data[0];
unsigned char* uStartLocation = (unsigned char*)m_pFrame->data[1];
unsigned char* vStartLocation = (unsigned char*)m_pFrame->data[2];
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
unsigned char* y = yStartLocation + ((y * width) + x);
unsigned char* u = uStartLocation + ((y * (width / 4)) + (x / 2));
unsigned char* v = vStartLocation + ((y * (width / 4)) + (x / 2));
//REF: https://en.wikipedia.org/wiki/YUV
// Write the texture pixel
dst[0] = y[0]; //R
dst[1] = u[0]; //G
dst[2] = v[0]; //B
dst[3] = 255; //A
// To next pixel
dst += 4;
// dst is the pointer to target texture RGBA data
}
}
The shader that converts YUV to RGB works perfectly and I've used it in multiple projects.
Now, I'm using the same code to decode on iOS platform. But for some reason the U and V values are now shifted:
Y Texture
U Texture
Is there anything that I'm missing for iOS or OpenGL specifically?
Any help greatly appreciated.
Thank You!
Please note that I filled R=G=B = Y for the first screenshot and U for the second.(if that makes sense)
Edit:
Heres the output that I'm getting:
Edit2:
Based on some research I think it may have something to do with Interlacing.
ref: Link
For now I've shifted to CPU based YUV-RGB conversion using sws_scale and it works fine.
The problem was on this line :
uStartLocation + ((y * (width / 4)) + (x / 2));
It should be
uStartLocation + (((y / 2) * (width / 2)) + (x / 2));
Since int rounding was causing the whole frame to shift. Very silly error trying to optimize calculations.
Hope it helps someone.

Overlay on 4 channel image

I have a image with 4 channels that i need to overlay it over a bunch of pictures. Over the pictures with 3 channels, the overlaying works great, but over the pictures that have an alpha channel, the background of the picture changes to black.
Original picture: http://img.blog.csdn.net/20130610074054484
Overlayed picture: http://imgur.com/mlVAN0A
This is the code that does the overlaying:
void overlayImage(const cv::Mat &background, const cv::Mat &foreground,
cv::Mat &output, cv::Point2i location)
{
background.copyTo(output);
for(int y = std::max(location.y , 0); y < background.rows; ++y)
{
int fY = y - location.y;
if(fY >= foreground.rows)
break;
for(int x = std::max(location.x, 0); x < background.cols; ++x)
{
int fX = x - location.x;
if(fX >= foreground.cols)
break;
double opacity = ((double)foreground.data[fY * foreground.step + fX * foreground.channels() + 3]) / 255.;
for(int c = 0; opacity > 0 && c < output.channels(); ++c)
{
unsigned char foregroundPx = foreground.data[fY * foreground.step + fX * foreground.channels() + c];
unsigned char backgroundPx = background.data[y * background.step + x * background.channels() + c];
output.data[y*output.step + output.channels()*x + c] =
backgroundPx * (1.-opacity) + foregroundPx * opacity;
}
}
}
}
This is because you use 1-opacity for the background image. If the opacity of the forground image is 0, the opacity of your backgroundpixel will be 1 instead of 0 which it is before.
You have to calc the result opacity fpr both images which can be 0 for both too.
Claus

Pixels in YUV image

I am using opencv to achieve object tracking. I read that YUV image is better option to use than RGB image. My problem is that I fail to understand about the YUV format although i spend much time read notes. Y is the brightness which i believe is calculated from the combination of R, G, B component.
My main problem is how can I access and manipulate the pixels in YUV image format. In RGB format its easy to access the component and therefore change it using simple operatin like
src.at<Vec3b>(j,i).val[0] = 0; for example
But this is not the case in YUV. I need help in accessing and changing the pixel values in YUV image. For example if pixel in RGB is red, then I want to only keep the corresponding pixel in YUV and the rest is removed. Please help me with this.
I would suggest operating on your image in HSV or LAB rather than RGB.
The raw image from the camera will be in YCbCr (sometimes called YUV, which I think is incorrect, but I may be wrong), and laid out in a way that resembles something like YUYV (repeating), so if you can convert directly from that to HSV, you will avoid additional copy and conversion operations which will save you some time. That may only matter to you if you're processing video or batches of images however.
Here's some C++ code for converting between YCbCr and RGB (one uses integer math, the other floating point):
Colour::bgr Colour::YCbCr::toBgrInt() const
{
int c0 = 22987;
int c1 = -11698;
int c2 = -5636;
int c3 = 29049;
int y = this->y;
int cb = this->cb - 128;
int cr = this->cr - 128;
int b = y + (((c3 * cb) + (1 << 13)) >> 14);
int g = y + (((c2 * cb + c1 * cr) + (1 << 13)) >> 14);
int r = y + (((c0 * cr) + (1 << 13)) >> 14);
if (r < 0)
r = 0;
else if (r > 255)
r = 255;
if (g < 0)
g = 0;
else if (g > 255)
g = 255;
if (b < 0)
b = 0;
else if (b > 255)
b = 255;
return Colour::bgr(b, g, r);
}
Colour::bgr Colour::YCbCr::toBgrFloat() const
{
float y = this->y;
float cb = this->cb;
float cr = this->cr;
int r = y + 1.40200 * (cr - 0x80);
int g = y - 0.34414 * (cb - 0x80) - 0.71414 * (cr - 0x80);
int b = y + 1.77200 * (cb - 0x80);
if (r < 0)
r = 0;
else if (r > 255)
r = 255;
if (g < 0)
g = 0;
else if (g > 255)
g = 255;
if (b < 0)
b = 0;
else if (b > 255)
b = 255;
return Colour::bgr(b, g, r);
}
And a conversion from BGR to HSV:
Colour::hsv Colour::bgr2hsv(bgr const& in)
{
Colour::hsv out;
int const hstep = 255 / 3; // Hue step size between red -> green -> blue
int min = in.r < in.g ? in.r : in.g;
min = min < in.b ? min : in.b;
int max = in.r > in.g ? in.r : in.g;
max = max > in.b ? max : in.b;
out.v = max; // v
int chroma = max - min;
if (max > 0)
{
out.s = 255 * chroma / max; // s
}
else
{
// r = g = b = 0 // s = 0, v is undefined
out.s = 0;
out.h = 0;
out.v = 0; // it's now undefined
return out;
}
if (chroma == 0)
{
out.h = 0;
return out;
}
const int chroma2 = chroma * 2;
int offset;
int diff;
if (in.r == max)
{
offset = 3 * hstep;
diff = in.g - in.b;
}
else if (in.g == max)
{
offset = hstep;
diff = in.b - in.r;
}
else
{
offset = 2 * hstep;
diff = in.r - in.g;
}
int h = offset + (diff * (hstep + 1)) / chroma2;
// Rotate such that red has hue 0
if (h >= 255)
h -= 255;
assert(h >= 0 && h < 256);
out.h = h;
return out;
Unfortunately I do not have code to do this in one step.
You can also use the built-in OpenCV functions for colour conversion.
cvtColor(img, img, CV_BGR2HSV);
Also the U and V components are calculated as linear combinations of RGB values. Then it means, that different intensities of red (R,0,0) are mapped to some (y*R + a,u*R + b, v*R + c), which again means that to detect "red" in YUV one can calculate if the distance of the pixel to that line determined by y,u,v,a,b,c (some of which are redundant) is close to zero. That's achievable with a single dot product. Then set the remaining pixels to the (0,128,128) in YUV space (I think that's R=0,G=0,B=0 in almost all varieties of YCrCb, YUV and such).
There are several YUV formats, but the common ones keep Y at the same resolution as the original image, but U and V are half size, and are saved as separate or interlaced planes/channels after the single channel Y image buffer.
This allows you to efficiently access Y as a 1-channel 8-bit greyscale image.
Access and manipulate pixels does not know the colorformat so the same code applies for color components Y U and V. If you need to access in RGB mode, best is probably calling cv::cvtColor for your region of interest first.

Locking a GDI+ Bitmap in Native C++?

I can find many examples on how to do this in managed c++ but none for unmanaged.
I want to get all the pixel data as efficiently as possible, but some of the scan0 stuff I would need more info about so I can properly iterate through the pixel data and get each rgba value from it.
right now I have this:
Bitmap *b = new Bitmap(filename);
if(b == NULL)
{
return 0;
}
UINT w,h;
w = b->GetWidth();
h = b->GetHeight();
Rect *r = new Rect(0,0,w,h);
BitmapData *lockdat;
b->LockBits(r,ImageLockModeRead,PixelFormatDontCare,lockdat);
delete(r);
if(w == 0 && h == 0)
{
return 0;
}
Color c;
std::vector<GLubyte> pdata(w * h * 4,0.0);
for (unsigned int i = 0; i < h; i++) {
for (unsigned int j = 0; j < w; j++) {
b->GetPixel(j,i,&c);
pdata[i * 4 * w + j * 4 + 0] = (GLubyte) c.GetR();
pdata[i * 4 * w + j * 4 + 1] = (GLubyte) c.GetG();
pdata[i * 4 * w + j * 4 + 2] = (GLubyte) c.GetB();
pdata[i * 4 * w + j * 4 + 3] = (GLubyte) c.GetA();
}
}
delete(b);
return CreateTexture(pdata,w,h);
How do I use lockdat to do the equivalent of getpixel?
Thanks
lockdat->Scan0 is a pointer to the pixel data of the bitmap. Note that you really do care what pixel format you ask for, PixelFormatDontCare won't do. Because how you use the pointer is affected by the pixel format. PixelFormat32bppARGB is the easiest, one pixel will be the size of an int, 4 bytes representing alpha, red, green and blue. And the stride will be equal to the width of the bitmap. Making it likely that a simple memcpy() will get the job done. Beware the bitmaps are stored upside-down.
Bitmap *m_image = new Bitmap(...) // a 24-bit RGB bitmap
BitmapData bmData;
Rect rect(0, 0, m_image->GetWidth(), m_image->GetHeight());
m_image->LockBits(&rect , ImageLockModeRead , PixelFormat24bppRGB,&bmData );
memcpy(your_bytes_buffer, bmData.Scan0, min(bmData.Height * bmData.Stride, your_buffer_size));
m_image->UnlockBits(&bmData);