Opencv C++ to C interface function conversion - c++

void doCorrectIntensityVariation(Mat& image)
{
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(19,19));
Mat closed;
morphologyEx(image, closed, MORPH_CLOSE, kernel);
image.convertTo(image, CV_32F); // divide requires floating-point
divide(image, closed, image, 1, CV_32F);
normalize(image, image, 0, 255, NORM_MINMAX);
image.convertTo(image, CV_8UC1); // convert back to unsigned int
}
inline void correctIntensityVariation(IplImage *img)
{
//Mat imgMat(img); copy the img
Mat imgMat;
imgMat = img; //no copy is done, imgMat is a header of img
doCorrectIntensityVariation(imgMat);
imshow("gamma corrected",imgMat); cvWaitKey(0);
}
When I call
cvShowImage ("normal", n_im); cvWaitKey (0);
correctIntensityVariation(n_im);//here n_im is IplImage*
cvShowImage ("After processed", n_im); cvWaitKey (0);
// here I require n_im for further processing
I wanted "After processed" to be same as that of "gamma corrected" but what I found "After processed" was not the same as that of "gamma corrected" but same as that of "normal" . Why?? What is going wrong??

A very simple wrapper should do the job
Cheetsheet of openCV
I rarely use the old api, because Mat are much more easier to deal with, and
they do not have performance penalty when compare with the old c api.Like the openCV
tutorial page say The main downside of the C++ interface is that many embedded development systems at the moment support only C. Therefore, unless you are targeting embedded platforms, there’s no point to using the old methods (unless you’re a masochist programmer and you’re asking for trouble).
openCV tutorial
cv::Mat to Ipl
Ipl to cv::Mat and Mat to Ipl
IplImage* pImg = cvLoadImage(“lena.jpg”);
cv::Mat img(pImg,0); //transform Ipl to Mat, 0 means do not copy
IplImage qImg; //not pointer, it is impossible to overload the operator of raw pointer
qImg = IplImage(img); //transform Mat to Ipl
Edit : I did a mistake earlier, if the Mat would be reallocated in the function, you need
to copy or try to steal the resource(I don't know how to do it yet) from the Mat.
Copy the data
void doCorrectIntensityVariation(cv::Mat& image)
{
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(19,19));
cv::Mat closed;
cv::morphologyEx(image, closed, cv::MORPH_CLOSE, kernel);
image.convertTo(image, CV_32F); // divide requires floating-point
cv::divide(image, closed, image, 1, CV_32F);
cv::normalize(image, image, 0, 255, cv::NORM_MINMAX);
image.convertTo(image, CV_8UC1); // convert back to unsigned int
}
//don't need to change the name of the function, the compiler treat
//these as different function in c++
void doCorrectIntensityVariation(IplImage **img)
{
cv::Mat imgMat;
imgMat = *img; //no copy is done, imgMat is a header of img
doCorrectIntensityVariation(imgMat);
IplImage* old = *img;
IplImage src = imgMat;
*img = cvCloneImage(&src);
cvReleaseImage(&old);
}
int main()
{
std::string const name = "onebit_31.png";
cv::Mat mat = cv::imread(name);
if(mat.data){
doCorrectIntensityVariation(mat);
cv::imshow("gamma corrected mat",mat);
cv::waitKey();
}
IplImage* templat = cvLoadImage(name.c_str(), 1);
if(templat){
doCorrectIntensityVariation(&templat);
cvShowImage("mainWin", templat);
// wait for a key
cvWaitKey(0);
cvReleaseImage(&templat);
}
return 0;
}
you could write a small function to alleviate the chores
void copy_mat_Ipl(cv::Mat const &src, IplImage **dst)
{
IplImage* old = *dst;
IplImage temp_src = src;
*dst = cvCloneImage(&temp_src);
cvReleaseImage(&old);
}
and call it in the function
void doCorrectIntensityVariation(IplImage **img)
{
cv::Mat imgMat;
imgMat = *img; //no copy is done, imgMat is a header of img
doCorrectIntensityVariation(imgMat);
copy_mat_to_Ipl(imgMat, img);
}
I will post how to "steal" the resource from Mat rather than copy after
I figure out a solid solution.Anyone know how to do it?

Related

Where does a Mat object store its data if it's not in data variable?

I need to transfer the image data from a Mat object (OpenCV) to a const unsigned char* within a ZXing::ImageView object; typically, I just use (assuming the object is named "object") object.data at my own risk and go from there if there are issues with the transfer. However, in this case, the data member variable is empty. This Mat object does produce an image with imshow though so I'm not sure where else to look. I have gone through the documentation but my limited experience and lack of knowledge keeps me from effectively going through it at a reasonable pace or in a relevant direction. Here is my code:
#include <opencv2/opencv.hpp>
#include <ZXing/ReadBarcode.h>
using namespace cv;
Mat applyThreshold(Mat gradient);
Mat erodeAnddilate(Mat threshold_applied);
void readBarCode(Mat dest);
int main() {
std::string file = "C:\\Users\\these\\Desktop\\cropped.JPG";
namedWindow("imageview", WINDOW_NORMAL);
Mat src = imread(file, IMREAD_COLOR);
Mat thresh_applied = applyThreshold(src);
Mat dest = erodeAnddilate(thresh_applied);
readBarCode(dest);
imshow("imageview", dest);
waitKey(0);
return 0;
}
Mat applyThreshold(Mat gradient) {
Mat dest, gray;
cvtColor(gradient, gray, COLOR_BGR2GRAY);
threshold(gray, dest, 0, 255, THRESH_BINARY + THRESH_OTSU);
return dest;
}
Mat erodeAnddilate(Mat threshold_applied) {
Mat dest;
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
morphologyEx(threshold_applied, dest, MORPH_CLOSE, kernel, Point(-1, -1), 2);
return dest;
}
void readBarCode(Mat dest) {
ZXing::ImageView test(dest.data, dest.size().width, dest.size().height, ZXing::ImageFormat::None);
ZXing::Result truth = ZXing::ReadBarcode(test);
int momentoftruth = 0;
}
The function readBarCode() is where the issue lies. And apologies for the probably terrible code everywhere else, I have a lot to learn. :)
EDIT: The accepted solution was the only one officially given, but all of the comments collectively helped me realize my error in thinking about the data variable in question. I see the data variable as a pointer now, and will take shallow vs deep copying into consideration as a potential solution. I have a better understanding of what's going on with my Mat object and consider my question answered. Thanks everyone.
Try to pass the reference to your Mat object to the functions, or if you want to copy the data for creating a new image, use explicitly the clone() method to get deep copy of your image.
Like either:
Mat applyThreshold(Mat& gradient) {
Mat dest, gray;
cvtColor(gradient, gray, COLOR_BGR2GRAY);
threshold(gray, dest, 0, 255, THRESH_BINARY + THRESH_OTSU);
return dest;
}
or:
// ...
Mat thresh_applied = applyThreshold(src.clone());
// ...

cv::imshow will not recognize an interleaved pointer wrapped in a cv::Mat

I have two functions that pass images using pointers
funct 1: Read a gray image from file, do image processing. Convert the processed image to color (3-channel). Wrapping it in a pointer.
funct 2: have an image pointer as input. Wrap it in a cv mat and show it. Do some other things.
funct 1:
cv::Mat imIn = cv::Mat(height, width,CV_16UC1);
cv::Mat outImage;
// read image
std::ifstream ifs{ imagesPathVec, std::ios::in | std::ios::binary };
if ( ifs.is_open() )
{
ifs.read( reinterpret_cast<char *>( imIn.data ), imIn.total() * imIn.elemSize() );
ifs.close();
}
imIn.convertTo(outImage, CV_32F);
//... some image processing applied to outImage
outImage.convertTo(outImage, CV_8UC1);
//wrapping the pointer outStreamBuffer in a cv::Mat.
cv::Mat outStream(height, width, CV_8UC3, static_cast<uint8_t*>(*outStreamBuffer));
// Try two methods to convert to color images.
//Method 1.
std::vector<cv::Mat> images(3);
images.at(0) = outImage;
images.at(1) = outImage;
images.at(2) = outImage;
cv::merge(images, outStream);
//Method 2.
cv::cvtColor(outImage, outStream, CV_GRAY2RGB);
Function 2
//wrap the incomming pointer inStreamBuffer in a Mat
cv::Mat inImage = cv::Mat(height, width, CV_8UC3, static_cast<uint8_t*>(*inStreamBuffer), width*3);
cv::imshow("m_inImage ", inImage);
cv::waitKey(10);
Since it is a gray image with 3 channels. I try to convert it to color by calling cvtColor() before the call to cv::imshow()
cv::cvtColor(inImage, inImage,CV_RGB2BGR);
but the results were the same.
The displayed image is
I would appreciate if you can help me to show the image correctly. Also help me to underestand what it is going on. Why opencv is not recognizing its own interleaved image?

QImage to cv::Mat convert strange behavior in comparison with imread

I have to transform QImage to cv::Mat, if I use technique described in similar topics, I receive different numbers of contours (7--8) and strange result matrix, but if I do
QImage im;
im.save ("tmp.bmp");
cv::Mat rImage;
rImage = cv::imread ("tmp.bmp", CV_LOAD_IMAGE_GRAYSCALE);
function findContours works fine and properly. What is the difference between these techniques and which way I can archive equal results between these approaches ?
Your code works for me.
int main(int argc, char *argv[]){
QImage img(QString("lena.bmp"));
QImage img2 = img.convertToFormat(QImage::Format_RGB32);
cv::Mat imageMat = qimage_to_cvmat_copy(img2, CV_8UC4);
cv::namedWindow("lena");
cv::imshow("lena", imageMat);
cv::waitKey(0);
}
cv::Mat qimage_to_cvmat_copy(const QImage &img, int format)
{
uchar* b = const_cast<uchar*> (img.bits ());
int c = img.bytesPerLine();
return cv::Mat(img.height(), img.width(), format, b, c).clone();
}
Make sure your Mat format is CV_8UC4 if your QImage format is Format_RGB32. You don't have to do a cvtColor or mixChannels.
All !
As mentioned above I used conversion QImage to cv::Mat as described here. My source code became something like this
QImage srcIm (argv[1]);
QImage img2 = srcIm.convertToFormat(QImage::Format_ARGB32);
Mat src_gray = QImageToCvMat (img2);
cvtColor (src_gray, src_gray1, CV_RGB2GRAY);
Mat bwimg = src_gray1.clone();// > 127;
vector<vector<Point> > contours;
findContours( bwimg, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE );
All works fine.

convert Qimage to cvMat 64FC3 format

i have searched a lot on the internet but i have only found how to convert Qimage to RGB format, i want to convert an Qimage to cv mat format CV_64FC3.
i have really bad results when i work with CV_8UC3
here is my code :
QImage myImage;
myImage.load("C://images//PolarImage300915163358.bmp");
QLabel myLabel;
myLabel.setPixmap(QPixmap::fromImage(myImage));
//myLabel.show();
cv::Mat image1 = QImage2Mat(myImage);
Mat img;
image1.convertTo(img, CV_64FC3, 1.0 / 255.0);
and here is the function that i used :
cv::Mat QImage2Mat(QImage const& src)
{
cv::Mat tmp(src.height(),src.width(),CV_8UC3,(uchar*)src.bits(),src.bytesPerLine());
cv::Mat result; // deep copy just in case (my lack of knowledge with open cv)
cvtColor(tmp, result,CV_BGR2RGB);
return result;
}
please help me i m new to both opencv and Qt
Not sure what you mean with bad results, but you are assuming that QImage also loads the image as OpenCV (BGR). In the documentation it tells you that they use ARGB.
So, knowing this you have 2 options:
Convert to QImage::Format_RGB888 the Qimage using the function convertToFormat and then this line cvtColor(tmp, result,CV_BGR2RGB); is not needed, since it will be already in RGB.
Use CV_8UC4 when creating the cv::Mat and then drop the first channel (channel alpha) using either split and join or mixchannels.
i have found what was going wrong, in fact, Qimage has a fourth channel for alpha so when you read the Qimage data you need to put it in CV_8UC4
here is the code :
Mat QImage2Mat(const QImage& src) {
cv::Mat mat = cv::Mat(src.height(), src.width(), CV_8UC4, (uchar*)src.bits(), src.bytesPerLine());
cv::Mat result = cv::Mat(mat.rows, mat.cols, CV_8UC3 );
int from_to[] = { 0,0, 1,1, 2,2 };
cv::mixChannels( &mat, 1, &result, 1, from_to, 3 );
return result;
}

Qt OpenCV - SIGSEGV when displaying transformed frames

I have an app that has to pull frames from video, transform one a little, transform one a lot, and simultaneously display them in GUI. In a worker thread, there's an OpenCV loop:
while(1) {
cv::VideoCapture kalibrowanyPlik;
kalibrowanyPlik.open(kalibracja.toStdString()); //open file from url
int maxFrames = kalibrowanyPlik.get(CV_CAP_PROP_FRAME_COUNT);
for(int i=0; i<maxFrames; i++) //I thought it crashed when finished reading the first time around
{
cv::Mat frame;
cv::Mat gray;
cv::Mat color;
kalibrowanyPlik.read(frame);
cv::cvtColor(frame, gray, CV_BGR2GRAY);
cv::cvtColor(frame, color, CV_BGR2RGB);
QImage image((uchar*)color.data, color.cols, color.rows,QImage::Format_RGB888);
QImage processedImage((uchar*)gray.data, gray.cols, gray.rows,QImage::Format_Indexed8);
emit progressChanged(image, processedImage);
QThread::msleep(50);
}
}
And this is how frames are placed in GUI
void MainWindow::onProgressChagned(QImage image, QImage processedImage) {
QPixmap processed = QPixmap::fromImage(processedImage);
processed = processed.scaledToHeight(379);
ui->labelHsv->clear();
ui->labelHsv->setPixmap(processed);
QPixmap original = QPixmap::fromImage(image); //debug points SIGSEGV here
original = original.scaledToHeight(379);
ui->labelKalibracja->clear();
ui->labelKalibracja->setPixmap(original);
}
The RGB image always crashes, grayscale image never crashes (tested). Why is the RGB image crashing?
edit: I've just discovered that if I change msleep(50) to msleep(100) it executes perfectly. But I don't want that. I need at least 25 frames per second, 10 is not acceptable... why would that cause a SIGSEGV
Standard issue. Problem is memory management!
See my other answer. In comments there is a good link.
So in your code QImage doesn't copy and doesn't take ownership of memory of matrix. And later on when matrix is destroyed and QImage tries access this memory (QImage is copied by creating shallow copy) you have a segfault.
Here is a code form this link (I've tweak it a bit), for some reason this site has some administration issues (some quota exceeded), that is why I'm pasting it here.
inline QImage cvMatToQImage( const cv::Mat &inMat )
{
switch ( inMat.type() )
{
// 8-bit, 4 channel
case CV_8UC4:
{
QImage image( inMat.data, inMat.cols, inMat.rows, inMat.step, QImage::Format_RGB32 );
QImage copy(image);
copy.bits(); //enforce deep copy
return copy;
}
// 8-bit, 3 channel
case CV_8UC3:
{
QImage image( inMat.data, inMat.cols, inMat.rows, inMat.step, QImage::Format_RGB888 );
return image.rgbSwapped();
}
// 8-bit, 1 channel
case CV_8UC1:
{
static QVector<QRgb> sColorTable;
// only create our color table once
if ( sColorTable.isEmpty() )
{
for ( int i = 0; i < 256; ++i )
sColorTable.push_back( qRgb( i, i, i ) );
}
QImage image( inMat.data, inMat.cols, inMat.rows, inMat.step, QImage::Format_Indexed8 );
image.setColorTable( sColorTable );
QImage copy(image);
copy.bits(); //enforce deep copy
return copy;
}
default:
qWarning() << "ASM::cvMatToQImage() - cv::Mat image type not handled in switch:" << inMat.type();
break;
}
return QImage();
}
Your code should utilize this functions like that:
while(1) {
cv::VideoCapture kalibrowanyPlik;
kalibrowanyPlik.open(kalibracja.toStdString()); //open file from url
int maxFrames = kalibrowanyPlik.get(CV_CAP_PROP_FRAME_COUNT);
for(int i=0; i<maxFrames; i++) //I thought it crashed when finished reading the first time around
{
cv::Mat frame;
cv::Mat gray;
kalibrowanyPlik.read(frame);
cv::cvtColor(frame, gray, CV_BGR2GRAY);
QImage image(cvMatToQImage(frame));
QImage processedImage(cvMatToQImage(gray));
emit progressChanged(image, processedImage);
QThread::msleep(10); // this is bad see comments below
}
}
Use of msleep is in 95% cases bad! Remove this loop and create slot which will be invoked by signal from QTimer.
Another solution will be to use a timer :
void ??::timerEvent(QTimerEvent*){
if(kalibrowanssky.isOpened())
cv::Mat frame;
cv::Mat gray;
cv::Mat color;
kalibrowanyPlik.read(frame);
cv::cvtColor(frame, gray, CV_BGR2GRAY);
cv::cvtColor(frame, color, CV_BGR2RGB);
ui->labelHsv->setPixmap(QPixmap::fromImage(Mat2QImage(color)));
ui->labelKalibracja->setPixmap(QPixmap::fromImage(Mat2QImage(gray)));
}
In your main :
cv::VideoCapture kalibrowanyPlik;
startTimer(1000/25); // 25 frames by second
And the function Mat2QImage (I found it here : how to convert an opencv cv::Mat to qimage) :
QImage ??::Mat2QImage(cv::Mat const& src) {
cv::Mat temp;
cvtColor(src, temp,CV_BGR2RGB);
QImage dest((const uchar *) temp.data, temp.cols, temp.rows, temp.step, QImage::Format_RGB888);
dest.bits();
return dest;
}