Converting cv::Mat image from BGR to YUV using ffmpeg - opencv

I am trying to convert BGR image into YUV420P, but when I try to view each of the YUV planes separately, this is what I see.
Shouldn't cv::Mat::data and AVFrame::data[0] be packed in the same way? I should be able to do a direct memcpy. Am I missing something?
Any ideas?
Mat frame;
VideoCapture cap;
if(!cap.open(0)){
return 0;
}
// capture a frame
cap >> frame;
if( frame.empty() ) return 0;
cv::Size s = frame.size();
int height = s.height;
int width = s.width;
// Creating two frames for conversion
AVFrame *pFrameYUV =av_frame_alloc();
AVFrame *pFrameBGR =av_frame_alloc();
// Determine required buffer size and allocate buffer for YUV frame
int numBytesYUV=av_image_get_buffer_size(AV_PIX_FMT_YUV420P, width,
height,1);
// Assign image buffers
avpicture_fill((AVPicture *)pFrameBGR, frame.data, AV_PIX_FMT_BGR24,
width, height);
uint8_t* bufferYUV=(uint8_t *)av_malloc(numBytesYUV*sizeof(uint8_t));
avpicture_fill((AVPicture *)pFrameYUV, bufferYUV, AV_PIX_FMT_YUV420P,
width, height);
// Initialise Software scaling context
struct SwsContext *sws_ctx = sws_getContext(width,
height,
AV_PIX_FMT_BGR24,
width,
height,
AV_PIX_FMT_YUV420P,
SWS_BILINEAR,
NULL,
NULL,
NULL
);
// Convert the image from its BGR to YUV
sws_scale(sws_ctx, (uint8_t const * const *)pFrameBGR->data,
pFrameYUV->linesize, 0, height,
pFrameYUV->data, pFrameYUV->linesize);
// Trying to see the different planes of YUV
Mat MY = Mat(height, width, CV_8UC1);
memcpy(MY.data,pFrameYUV->data[0], height*width);
imshow("Test1", MY); // fail
Mat MU = Mat(height/2, width/2, CV_8UC1);
memcpy(MU.data,pFrameYUV->data[1], height*width/4);
imshow("Test2", MU); // fail
Mat MV = Mat(height/2, width/2, CV_8UC1);
memcpy(MV.data,pFrameYUV->data[2], height*width/4);
imshow("Test3", MV); // fail
waitKey(0); // Wait for a keystroke in the window

For sws_scale() third parameter should not be pFrameYUV->linesize but rather pFrameBGR->linesize, i.e. pointer to width*3.

Related

imagemagick : saving MagickExportImagePixels's output blob to gray image file?

zbar engine sample source(zbarimg.c) shows the following.
https://github.com/ZBar/ZBar/blob/master/zbarimg/zbarimg.c
size_t bloblen = width * height;
unsigned char *blobdata = malloc(bloblen);
MagickExportImagePixels(images, 0, 0, width, height, "I", CharPixel, blobdata);
I'd like to see the blobdata.
How can I save the blobdata to file?
I made save_imgdata function to save blobdata.
int save_imgdata(char* imgf, int width, int height, char *raw)
{
PixelWand *p_wand = NULL;
PixelIterator *iterator = NULL;
PixelWand **pixels = NULL;
unsigned long x, y;
char hex[128];
//MagickWandGenesis();
p_wand = NewPixelWand();
PixelSetColor(p_wand, "gray");
//PixelSetColor(p_wand, "white");
MagickWand *m_wand = NewMagickWand(); //CORE_RL_wand_.lib;
MagickSetImageDepth(m_wand, 8);
MagickNewImage(m_wand, width, height, p_wand);
// Get a new pixel iterator
iterator = NewPixelIterator(m_wand);
for (y = 0; y<height; y++) {
// Get the next row of the image as an array of PixelWands
pixels = PixelGetNextIteratorRow(iterator, &x);
// Set the row of wands to a simple gray scale gradient
for (x = 0; x<width; x++) {
sprintf(hex, "#%02x", *raw++);
//sprintf(hex, "#%02%x02%x02x", *raw, *raw, *raw); raw++;
PixelSetColor(pixels[x], hex);
}
// Sync writes the pixels back to the m_wand
PixelSyncIterator(iterator);
}
MagickWriteImage(m_wand, imgf);
DestroyMagickWand(m_wand);
return 0;
}
The call of save_imgdata("imgw.bmp", width, height, blobdata)
save 24bpp image.
What's wrong of save_imgdata?
I want it saves 8bpp gray image file.
Don't bother iterating and building dynamic color/pixel values -- It's slow and resource intensive. If the data came from an export method, than use the import method to restore.
int save_imgdata(char* imgf, int width, int height, void * raw)
{
MagickWand * wand;
PixelWand * bgcolor;
bgcolor = NewPixelWand();
PixelSetColor(bgcolor, "WHITE");
wand = NewMagickWand();
MagickNewImage(wand, width, height, bgcolor);
bgcolor = DestroyPixelWand(bgcolor);
MagickSetImageDepth(wand, 8);
MagickSetImageColorspace(wand, GRAYColorspace);
MagickImportImagePixels(wand, 0, 0, width, height, "I", CharPixel, raw);
MagickQuantizeImage(wand,
256, // Reduce to 8bpp
GRAYColorspace, // Match colorspace
0, // Calculate optimal tree depth
MagickTrue, // Use dither ? This changes in IM-7
MagickFalse); // Messure Error
MagickWriteImage(wand, imgf);
wand = DestroyMagickWand(wand);
return 0;
}

Converting from YUV colour space to RGB using OpenCV

I am trying to convert a YUV image to RGB using OpenCV. I am a complete novice at this. I have created a function which takes a YUV image as source and converts it into RGB. It is like this :
void ConvertYUVtoRGBA(const unsigned char *src, unsigned char *dest, int width, int height)
{
cv::Mat myuv(height + height/2, width, CV_8UC1, &src);
cv::Mat mrgb(height, width, CV_8UC4, &dest);
cv::cvtColor(myuv, mrgb, CV_YCrCb2RGB);
return;
}
Should this work? Do I need to convert the Mat into char* again? I am in a loss and any help will be greatly appreciated.
There is not enough detail in your question to give a certain answer but below is my best guess. I'll assume you want RGBA output (not RGB, BGR or BGRA) and that your YUV is yuv420sp (as this is what comes out of an Android camera, and it is consistent with your Mat sizes)
void ConvertYUVtoRGBA(const unsigned char *src, unsigned char *dest, int width, int height)
{
//cv::Mat myuv(height + height/2, width, CV_8UC1, &src);
cv::Mat myuv(height + height/2, width, CV_8UC1, src); // pass buffer pointer, not its address
//cv::Mat mrgb(height, width, CV_8UC4, &dest);
cv::Mat mrgb(height, width, CV_8UC4, dest);
//cv::cvtColor(myuv, mrgb, CV_YCrCb2RGB);
cv::cvtColor(myuv, mrgb, CV_YUV2RGBA_NV21); // are you sure you don't want BGRA?
return;
}
Do I need to convert the Mat into char again?*
No the Mat mrgb is a wrapper around dest and, the way you have arranged it, the RGBA data will written directly into the dest buffer.

How to deal with 8UC3 and 8UC4 simultaneously in android ndk

My code is working fine with showing brightness in the image using below code
jint* _in = env->GetIntArrayElements(in, 0);
jint* _out = env->GetIntArrayElements(out, 0);
Mat mSrc(height, width, CV_8UC4, (unsigned char*)_in);
Mat bgra(height, width, CV_8UC4, (unsigned char*)_out);
vector<Mat> sChannels;
split(mSrc, sChannels);
for(int i=0; i<sChannels.size(); i++)
{
Mat channel = sChannels[i];
equalizeHist(channel, channel);
}
merge(sChannels, bgra);
env->ReleaseIntArrayElements(in, _in, 0);
env->ReleaseIntArrayElements(out, _out, 0);
jint retVal;
int ret = 1;
retVal = jint(retVal);
return retVal;
It work for me too for changing image into grayscale but in this way :
Mat mSrc(height, width, CV_8UC4, (unsigned char*)_in);
Mat gray(height, width, CV_8UC1);
Mat bgra(height, width, CV_8UC4, (unsigned char*)_out);
cvtColor(mSrc , gray , CV_BGRA2GRAY);
cvtColor(gray , bgra , CV_GRAY2BGRA);
But when i am trying to use bilateralfilter with it , which only work with 3 channels as given here , how to deal with it ? because java bitmap accept RGBA format ,and when i change the above into
Mat mSrc(height, width, CV_8UC3, (unsigned char*)_in);
Mat bgra(height, width, CV_8UC3, (unsigned char*)_in);
somehow bilateral filter show me output , but what if I have apply all these filter on one image ? how can I handle this problem ? because there may be other algorithms too which only deal with 3 channels.
If you need to convert CV_8UC3 to CV_8UC4 you just need to call:
cvtColor(src, dst, CV_BGR2BGRA);
since it outputs a 4-channel image (R, G, B and Alpha)
Inversely,
cvtColor(src, dst, CV_BGRA2BGR);
should convert CV_8UC4 to CV_8UC3

How do you find a region of a certain color within an image on iOS?

I'm doing an image processing iOS app, where we have a large image (eg:size will be 2000x2000). Assume that image is completely black, except one part of the image is a different color (lets say the size of that region is 200x200).
SI want to calculate the start and end position of that differently coloured region. How can I achieve this?
Here's a simple way to allow the CPU to get pixel values from a UIImage. The steps are
allocate a buffer for the pixels
create a bitmap memory context using the buffer as the backing store
draw the image into the context (writes the pixels into the buffer)
examine the pixels in the buffer
free the buffer and associated resources
- (void)processImage:(UIImage *)input
{
int width = input.size.width;
int height = input.size.height;
// allocate the pixel buffer
uint32_t *pixelBuffer = calloc( width * height, sizeof(uint32_t) );
// create a context with RGBA pixels
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate( pixelBuffer, width, height, 8, width * sizeof(uint32_t), colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast );
// invert the y-axis, so that increasing y is down
CGContextScaleCTM( context, 1.0, -1.0 );
CGContextTranslateCTM( context, 0, -height );
// draw the image into the pixel buffer
UIGraphicsPushContext( context );
[input drawAtPoint:CGPointZero];
UIGraphicsPopContext();
// scan the image
int x, y;
uint8_t r, g, b, a;
uint8_t *pixel = (uint8_t *)pixelBuffer;
for ( y = 0; y < height; y++ )
for ( x = 0; x < height; x++ )
{
r = pixel[0];
g = pixel[1];
b = pixel[2];
a = pixel[3];
// do something with the pixel value here
pixel += 4;
}
// release the resources
CGContextRelease( context );
CGColorSpaceRelease( colorSpace );
free( pixelBuffer );
}

Search multiple colours simultaneously with opencv and c++

I am trying to make the code below search multiple colours at the same time e.g. red, yellow and orange. I have added masks to the code but a cant get them to apply properly what am i doing wrong I am not sure if the line to add the masks to the frame is in the right place or not.
#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
using namespace std;
using namespace cv::gpu;
IplImage* GetThresholdedImage(IplImage* imgHSV){
IplImage* imgThresh=cvCreateImage(cvGetSize(imgHSV),IPL_DEPTH_8U, 1);
cvInRangeS(imgHSV, cvScalar(170,160,60), cvScalar(180,255,256), imgThresh);
return imgThresh;
}
int main(){
//Char Firetype = type;
CvCapture* capture = cvCaptureFromCAM(1);
if(!capture){
printf("Capture failure\n");
return -1;
}
IplImage* frame=0;
cvNamedWindow("Video");
cvNamedWindow("Fire");
cvNamedWindow("Info");
//iterate through each frame of the video
while(true){
frame = cvQueryFrame(capture);
if(!frame) break;
frame=cvCloneImage(frame);
cvSmooth(frame, frame, CV_GAUSSIAN,3,3); //smooth the original image using Gaussian kernel
cv::Mat imgMat(frame);
cv::Mat mask1;
cv::inRange(imgMat, cv::Scalar(20, 100, 100), cv::Scalar(30, 255, 255), mask1);
cv::Mat mask2;
cv::inRange(imgMat, cv::Scalar(170,160,60), cv::Scalar(180,255,256), mask2);
cv::Mat mask3;
cv::inRange(imgMat, cv::Scalar(70,160,60), cv::Scalar(90,255,256), mask3);
// combine them
cv::Mat mask_combined = mask1 | mask2 | mask3;
// now since our frame from the camera is bgr, we have to convert our mask to 3 channels:
cv::Mat mask_rgb;
cv::cvtColor( mask_combined, mask_rgb, CV_GRAY2BGR );
IplImage framemask = imgMat & mask_rgb;
IplImage* imgHSV = cvCreateImage(cvGetSize(framemask), IPL_DEPTH_8U, 3);
cvCvtColor(frame, imgHSV, CV_BGR2HSV); //Change the color format from BGR to HSV
//This function threshold the HSV image and create a binary image
// function below to get b&w image
IplImage* imgThresh = GetThresholdedImage(imgHSV);
cvSmooth(imgThresh, imgThresh, CV_GAUSSIAN,3,3); //smooth the binary image using Gaussian kernel
cvShowImage("Fire", imgThresh);
cvShowImage("Video", frame);
// cvNamedWindow("Info", Firetype);
cvReleaseImage(&imgHSV);
cvReleaseImage(&imgThresh);
cvReleaseImage(&frame);
//Wait 50mS
int c = cvWaitKey(10);
//If 'ESC' is pressed, break the loop
if((char)c==27 ) break;
}
cvDestroyAllWindows() ;
cvReleaseCapture(&capture);
return 0;
}
tl:dr;
use bitwise-or to combine binary masks,
bitwise_and to apply them
cv::Mat mask1;
cv::inRange( hsv, cv::Scalar(20, 100, 100), cv::Scalar(30, 255, 255), mask1);
cv::Mat mask2;
cv::inRange( hsv, cv::Scalar(170,160,60), cv::Scalar(180,255,256), mask2);
cv::Mat mask3;
cv::inRange( hsv, cv::Scalar(70,160,60), cv::Scalar(90,255,256), mask3);
// combine them
cv::Mat mask_combined = mask1 | mask2 | mask3;
// now since our frame from the camera is bgr, we have to convert our mask to 3 channels:
cv::Mat mask_rgb;
cv::cvtColor( mask_combined, mask_rgb, CV_GRAY2BGR );
// finally, apply the mask to our image
frame = frame & mask_rgb;

Resources