I'm very new at C++ and I'm trying to create a DLL which uses OpenCV library.
My DLL gets a raw image from other application and creates a MAT from the application's memory buffer. I send the buffer's address, which has a raw image, to the DLL and get raw image to OpenCV. This part works.
But after processing image with OpenCV, I can't write raw image to same memory address.
This is the code snippet that I've tried:
fn_export double createImage(char* address double width, double height) {
unsigned char* pBuffer = (unsigned char*)address;
memcpy(&pBuffer,&address, sizeof(pBuffer));
cv::Mat img = cv::Mat(height,width, CV_8UC4, pBuffer);
cv::imshow("Original", img);
memcpy(&address, &img.data[0], sizeof(address));
return 1;
}
char* address is memory address from my application. Other application's buffer doesn't change this way. Anybody has any advice about this situation?
Ok. I solved this issue ;
Mat img = Mat(height, width, CV_8UC4, address);
cv::imshow("Image from GM", img);
// same image copy to buffer back;
memcpy(&address[0], &img.data[0], width*height*4.);
Related
I am working on a project, where I want to process my images using C++ OpenCV.
For simplicity's sake, I just want to convert Uint8List to cv::Mat and back.
Following this tutorial, I managed to make a pipeline that doesn't crash the app. Specifically:
I created a function in a .cpp that takes the pointer to my Uint8List, rawBytes, and encodes it as a .jpg:
int encodeIm(int h, int w, uchar *rawBytes, uchar **encodedOutput) {
cv::Mat img = cv::Mat(h, w, CV_8UC3, rawBytes); //CV_8UC3
vector<uchar> buf;
cv:imencode(".jpg", img, buf); // save output into buf. Note that Dart Image.memory can process either .png or .jpg, which is why we're doing this encoding
*encodedOutput = (unsigned char *) malloc(buf.size());
for (int i=0; i < buf.size(); i++)
(*encodedOutput)[i] = buf[i];
return (int) buf.size();
}
Then I wrote a function in a .dart that calls my c++ encodeIm(int h, int w, uchar *rawBytes, uchar **encodedOutput):
//allocate memory heap for the image
Pointer<Uint8> imgPtr = malloc.allocate(imgBytes.lengthInBytes);
//allocate just 8 bytes to store a pointer that will be malloced in C++ that points to our variably sized encoded image
Pointer<Pointer<Uint8>> encodedImgPtr = malloc.allocate(8);
//copy the image data into the memory heap we just allocated
imgPtr.asTypedList(imgBytes.length).setAll(0, imgBytes);
//c++ image processing
//image in memory heap -> processing... -> processed image in memory heap
int encodedImgLen = _encodeIm(height, width, imgPtr, encodedImgPtr);
//
//retrieve the image data from the memory heap
Pointer<Uint8> cppPointer = encodedImgPtr.elementAt(0).value;
Uint8List encodedImBytes = cppPointer.asTypedList(encodedImgLen);
//myImg = Image.memory(encodedImBytes);
return encodedImBytes;
//free memory heap
//malloc.free(imgPtr);
//malloc.free(cppPointer);
//malloc.free(encodedImgPtr); // always frees 8 bytes
}
Then I linked c++ with dart via:
final DynamicLibrary nativeLib = Platform.isAndroid
? DynamicLibrary.open("libnative_opencv.so")
: DynamicLibrary.process();
final int Function(int height, int width, Pointer<Uint8> bytes, Pointer<Pointer<Uint8>> encodedOutput)
_encodeIm = nativeLib
.lookup<NativeFunction<Int32 Function(Int32 height, Int32 width,
Pointer<Uint8> bytes, Pointer<Pointer<Uint8>> encodedOutput)>>('encodeIm').asFunction();
And finally I show the result in Flutter via:
Image.memory(...)
Now, the pipeline doesn't crash, which means I haven't goofed up memory handling completely, but it doesn't return the original image either, which means I did mess up somewhere.
Original image:
Pipeline output:
Thanks to Richard Heap's guidance in the comments, I managed to fix the pipeline by changing my matrix definition from
cv::Mat img = cv::Mat(h, w, CV_8UC3, rawBytes);
to
vector<uint8_t> buffer(rawBytes, rawBytes + inBytesCount);
Mat img = imdecode(buffer, IMREAD_COLOR);
where inBytesCount is the length of imgBytes.
I am working on image processing and developed camera wrappers with OpenCV for a RGB and a monochrome camera. Now I have to use an existing algorithm that works with CUDA to process those two camera image streams. For that I have to copy the Mat images to my device (the algorithm does not take gpumat). I use cv::Mat::ptr to access the data of the images. When I use cudaMemcpy2D to get the image back to the host, I receive a dark image (zeros only) for the RGB image. Even when I use cudaMemcpy2D to just load it to the device and bring it back in the next step with cudaMemcpy2D it won't work (by that I mean I don't do any image processing in between). It works fine for the mono image though:
width = 1920; (image dimensions are the same for mono and BGR)
height = 1080;
Mat mat_mono(height, width, CV_8UC1);
Mat mat_mono_disp(height, width, CV_8UC1);
size_t pitch_mono;
uint8_t* image_mono_gpu,
size_t matrixLenMono = width;
cudaMallocPitch(&image_mono_gpu, &pitch_mono, width, height);
mat_mono = MonoCamera.CaptureMat(1); // wrapper for the mono camera that grabs the image
// copy to device
cudaMemcpy2D(image_mono_gpu, pitch_mono, mat_mono.ptr(), width, matrixLenMono, height, cudaMemcpyHostToDevice);
// copy back to host
cudaMemcpy2D(mat_mono_disp.ptr(), matrixLenMono, image_mono_gpu, pitch_mono, matrixLenMono, height, cudaMemcpyDeviceToHost);
namedWindow("Display window", WINDOW_AUTOSIZE);
imshow("Display window", mat_mono_disp);
This is the code for the RGB (or rather BGR) image, where I only receive a dark image after retrieving the image from the device:
Mat mat_BGR(height, width, CV_8UC3);
Mat mat_BGR_disp(height, width, CV_8UC3);
size_t pitch_BGR;
uint8_t* image_BGR_gpu,
size_t matrixLenBGR = width * 3;
cudaMallocPitch(&image_BGR_gpu, &pitch_BGR, matrixLenBGR, height);
mat_BGR = RGBCamera.CaptureMat(1); // wrapper for the RGB camera that grabs the image
// copy to device
cudaMemcpy2D(image_BGR_gpu, pitch_BGR, mat_BGR.ptr(), width, matrixLenBGR, height, cudaMemcpyHostToDevice);
// copy back to host
cudaMemcpy2D(mat_BGR_disp.ptr(), matrixLenBGR, image_BGR_gpu, pitch_BGR, matrixLenBGR, height, cudaMemcpyDeviceToHost);
namedWindow("Display window", WINDOW_AUTOSIZE);
imshow("Display window", mat_BGR_disp);
Does this mean that using cv::Mat:ptr with a mono image works as this is a special case? I don't know what I have to consider additionally when using the BGR image instead.
As pointed out in a previous answer, when performing 2D memory copy of OpenCV Mat to device memory allocated using cudaMallocPitch ( or any strided 2D memory ), we have to use the step member of the OpenCV Mat to specify the alignment of each row.
In the provided code, the correct way would be to use mat_BGR.step instead of width in the 4th argument of cudaMemcpy2D.
cudaMemcpy2D(image_BGR_gpu, pitch_BGR, mat_BGR.ptr(), mat_BGR.step, matrixLenBGR, height, cudaMemcpyHostToDevice);
^^^^
I have a uchar* raw from an API which represents an image raw data. The width, height and number of channels of this raw is already known. I have already pre-allocated an cv::Mat (OpenCV) with this width and height.
My question is - how is it possible to set raw into this cv::Mat. I would like to copy raw into cv::Mat instead of just switching pointers. Is there a function to accomplish this or I need to do so manually myself?
I guess it isn't the most sophisticated way but it should work:
uchar* raw;
cv::Mat image(size, type, raw);
image = image.clone();
cv::Mat mat(cv::Size(width, height), CV_8UC1, raw, cv::Mat::AUTO_STEP);
copiedImage = mat.clone();
I'm trying to show LiveView image in real time. I use EDSDK 2.14 + Qt5 + opencv+mingw32 under Windows. I'm not very sophisticated in image processing so now I have the following problem. I use example from Canon EDSDK and all was ok until this part of code:
//
// Display image
//
I googled a lot of examples but all of them was written on C# or MFC or VB. Also I found advise to use libjpegTurbo for decompressing image and then showing it using opencv. I tried to use libjpegTurbo but failed to undestand what to do :(. Maybe somebody here have code example of the conversion LiveView stream to opencv Mat or QImage (because I use Qt)?
Here is what worked for me after following the SAMPLE 10 from the Canon EDSDK Reference. It's a starting point for a more robust solution.
In the downloadEvfData function, I replaced the "Display Image" part by the code bellow:
unsigned char *data = NULL;
EdsUInt32 size = 0;
EdsSize coords ;
// get image coordinates
EdsGetPropertyData(evfImage, kEdsPropsID_Evf_CoordinateSystem, 0, sizeof(coords), &coords);
// get buffer pointer and size
EdsGetPointer(stream, (EdsVoid**)&data);
EdsGetLenth(stream, &size);
//
// release stream and evfImage
//
// create mat object
Mat img(coords.height, coords.width, CV_8U, data);
image = imdecode(img, CV_LOAD_IMAGE_COLOR);
I've also changed the function signature:
EdsError downloadEvfData(EdsCameraRef camera, Mat& image)
And in the main function:
Mat image;
namedWindow("main", WINDOW_NORMAL);
startLiveView(camera);
for(;;) {
dowloadEvfData(camera, image);
imshow("main", image);
if (waitkey(10) >= 0);
break;
}
Based on the Canon EDSDKs example, you may append your EdsStreamRef 'stream' data with its correct length into a QByteArray. Then, use for example the following to parse the raw data from the QByteArray as a JPG into a new QImage:
QImage my_image = QImage::fromData(limagedata,"JPG"); Once it's in a QImage you can convert it into a OpenCV cv::Mat (see How to convert QImage to opencv Mat)
Well it depends on the format of the liveview-stream.
There must be some kind of delimiter in it and you need then to convert each image and update your QImage with it.
Check out this tutorial for more information: Canon EDSDK Tutorial in C#
QImage img = QImage::fromData(data, length, "JPG");
m_image = QImageToMat(img);
// -----------------------------------------
cv::Mat MainWindow::QImageToMat(QImage& src)
{
cv::Mat tmp(src.height(),src.width(),CV_8UC4,(uchar*)src.bits(),src.bytesPerLine());
cv::Mat result = tmp.clone();
return result;
}
// -------------------------
void MainWindow::ShowVideo()
{
namedWindow("yunhu",WINDOW_NORMAL);
while(1)
{
requestLiveViewImage();
if(m_image.data != NULL)
{
imshow("yunhu", m_image);
cvWaitKey(50);
}
}
}
IplImage* img = cvLoadImage("something.jpg");
IplImage* src = cvLoadImage("src.jpg");
cvSub(src, img, img);
But the size of the source image is different from img.
Is there any opencv function to resize it to the img size?
You can use cvResize. Or better use c++ interface (eg cv::Mat instead of IplImage and cv::imread instead of cvLoadImage) and then use cv::resize which handles memory allocation and deallocation itself.
The two functions you need are documented here:
imread: read an image from disk.
Image resizing: resize to just any size.
In short:
// Load images in the C++ format
cv::Mat img = cv::imread("something.jpg");
cv::Mat src = cv::imread("src.jpg");
// Resize src so that is has the same size as img
cv::resize(src, src, img.size());
And please, please, stop using the old and completely deprecated IplImage* classes
For your information, the python equivalent is:
imageBuffer = cv.LoadImage( strSrc )
nW = new X size
nH = new Y size
smallerImage = cv.CreateImage( (nH, nW), imageBuffer.depth, imageBuffer.nChannels )
cv.Resize( imageBuffer, smallerImage , interpolation=cv.CV_INTER_CUBIC )
cv.SaveImage( strDst, smallerImage )
Make a useful function like this:
IplImage* img_resize(IplImage* src_img, int new_width,int new_height)
{
IplImage* des_img;
des_img=cvCreateImage(cvSize(new_width,new_height),src_img->depth,src_img->nChannels);
cvResize(src_img,des_img,CV_INTER_LINEAR);
return des_img;
}
You can use CvInvoke.Resize for Emgu.CV 3.0
e.g
CvInvoke.Resize(inputImage, outputImage, new System.Drawing.Size(100, 100), 0, 0, Inter.Cubic);
Details are here