I was trying to find a way how to make default parameter of Mat type, but it was complicated to make. But, today I found out this code OutputArray _hist = Mat() and I thought this could be simply a default parameter of Mat type. Therefore, I could make this code and it worked well, but I couldn't still understand one thing.
int myGetHistogram(InputArray _src, OutputArray _hist = Mat())
{
Mat src = _src.getMat();
_hist.create(512,512,CV_8U);
Mat histImage = _hist.getMat();
...
rectangle(histImage, max_pt1, max_pt2, Scalar(0), -1);
return max_pt1.x/(histImage.cols/256);
}
In this code, this error message shows up.
OpenCV Error: Assertion failed (!fixedSize() || ((Mat)obj)->size.operator()() == _sz)*
If I set the default parameter as OutputArray _hist = Mat(512,512,CV_8U) instead of OutputArray _hist = Mat(), then the error is removed.
Why does this problem happen?
In either case you're creating an OutputArray from a temporary Mat (i.e. this constructor), so you won't be able to change the size or datatype.
Take inspiration from the OpenCV code. Use cv::noArray() to make the output parameter optional, and then cv::OutputArray::needed to determine how to initialize your cv::Mat histImage.
#include <opencv2/opencv.hpp>
int myGetHistogram(cv::InputArray _src, cv::OutputArray _hist = cv::noArray())
{
cv::Mat src = _src.getMat();
cv::Size const HISTOGRAM_SIZE(512, 512);
cv::Mat histImage;
if (_hist.needed()) {
_hist.create(HISTOGRAM_SIZE, CV_8U);
histImage = _hist.getMat();
} else {
histImage = cv::Mat(HISTOGRAM_SIZE, CV_8UC1);
}
// ... whatever
return 1;
}
int main()
{
cv::Mat a(4, 4, CV_8UC1);
cv::Mat b;
myGetHistogram(a);
myGetHistogram(a, b);
return 0;
}
Related
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());
// ...
I have a problem with this code:
The problem is when I see the image original, is modified by "borrarFondo()" but this function is called from "segmentarHoja" and here entry img by value, but img modifies.
void borrarFondo(Mat& img){
img = ~img;
Mat background;
medianBlur(img, background, 45);
GaussianBlur(background, background, Size(203,203),101,101);
img = img - background;
img = ~img;
}
void segmentarHoja(Mat img, Mat& imsheet){
Mat imgbw;
borrarFondo(img); //borrarFondo is called from here where img is a copy
cvtColor(img, imgbw, CV_BGR2GRAY);
threshold(imgbw, imgbw, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
Mat element = getStructuringElement(MORPH_ELLIPSE, Size(21,21));
erode(imgbw, imgbw, element);
vector<vector<Point> > contoursSheet;
findContours(imgbw, contoursSheet, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
vector<Rect> boundSheet(contoursSheet.size());
int largest_area=0;
for( int i = 0; i< contoursSheet.size(); i++ )
{
double a= contourArea( contoursSheet[i],false);
if(a>largest_area){
largest_area=a;
boundSheet[i] = boundingRect(contoursSheet[i]);
imsheet=img(boundSheet[i]).clone();
}
}
borrarFondo(imsheet);
}
int main()
{
Mat imsheet;
image= imread("c:/imagen.jpg");
segmentarHoja(image, imsheet);
imshow("imsheet",imsheet);
imshow("imagen",image); //original image by amending borrarFondo
waitKey(0);
}
I don't want to change original image
opencv Mat is a counted reference (i.e. like std::shared_ptr, except different syntax) where copy construction or assignment does not copy. use the clone method to copy. read the documentation, always a good idea.
if you're doing something like this:
Mat a;
Mat b = a;
or like this:
void func(Mat m) {...}
or :
vector<Mat> vm;
vm.push_back(m);
all of it is a shallow copy. the Mat header will be a copy, the pointers inside, too.
so, e.g. in the 1st example, b and a share the same size and data members
this might explain, why passing a Mat by value still results in pixels manipulated from the 'shallow' copy.
to avoid that you will have to do a 'deep' copy instead:
Mat c = a.clone(); // c has its own pixels now.
and again, if you don't want your Mat to be manipulated, pass it as a const Mat & be very careful about how you use it, as illustrated below.
#include <opencv2/opencv.hpp>
void foo( cv::Mat const& image )
{
cv::Mat result = image;
cv::ellipse(
result, // img
cv::Point( 300, 300 ), // center
cv::Size( 50, 50 ), // axes (bounding box size)
0.0, // angle
0.0, // startAngle
360.0, // endAngle
cv::Scalar_<int>( 0, 0, 255 ), // color
6 // thickness
);
}
auto main() -> int
{
auto window_name = "Display";
cv::Mat lenna = cv::imread( "lenna.png" );
foo( lenna );
imshow( window_name, lenna );
cv::waitKey( 0 );
}
The Mat const& lied about mutability, and Lenna’s nose is correspondingly long, here marked by a big fat circle placed by the foo function above:
I am not very experinced when it comes to working with C++ and I was given some code from Andrey Smorodov when I use the method it is not manipulating the image. I believe it is being passed by value and once the method is done running the variables are gone. Can someone please tell me if passing by references vs value is what is wrong? When I comment out the method it does not change the result image.
Method:
void CalcBlockMeanVariance(cv::Mat Img,cv::Mat Res,float blockSide=21) // blockSide - the parameter (set greater for larger font on image)
{
cv::Mat I;
Img.convertTo(I,CV_32FC1);
Res=cv::Mat::zeros(Img.rows/blockSide,Img.cols/blockSide,CV_32FC1);
cv::Mat inpaintmask;
cv::Mat patch;
cv::Mat smallImg;
cv::Scalar m,s;
for(int i = 0;i < Img.rows-blockSide;i+=blockSide)
{
for (int j=0;j<Img.cols-blockSide;j+=blockSide)
{
patch=I(cv::Rect(j,i,blockSide,blockSide));
cv::meanStdDev(patch,m,s);
if(s[0]>0.01) // Thresholding parameter (set smaller for lower contrast image)
{
Res.at<float>(i/blockSide,j/blockSide)=m[0];
}else
{
Res.at<float>(i/blockSide,j/blockSide)=0;
}
}
}
cv::resize(I,smallImg,Res.size());
cv::threshold(Res,inpaintmask,0.02,1.0,cv::THRESH_BINARY);
cv::Mat inpainted;
smallImg.convertTo(smallImg,CV_8UC1,255);
inpaintmask.convertTo(inpaintmask,CV_8UC1);
inpaint(smallImg, inpaintmask, inpainted, 5, cv::INPAINT_TELEA);
cv::resize(inpainted,Res,Img.size());
Res.convertTo(Res,CV_32FC1,1.0/255.0);
}
Calling the method:
cv::Mat cvImage = [self cvMatFromUIImage:image];
cv::Mat res;
cv::cvtColor(cvImage, cvImage, CV_RGB2GRAY);
cvImage.convertTo(cvImage,CV_32FC1,1.0/255.0);
//CalcBlockMeanVariance(cvImage,res);
res=1.0-res;
res=cvImage+res;
cv::threshold(res,res, 0.85, 1, cv::THRESH_BINARY);
cv::resize(res, res, cv::Size(res.cols/2,res.rows/2));
cvImage.convertTo(cvImage,CV_8UC3,255.0);
_endImage = [self UIImageFromCVMat:cvImage];
Could I be losing any data when converting back to a UIImage?
Here is my resulting image:
The result Andrey got using this method:
Could anyone explain why I might be getting such a different result compared to Andrey?
Thanks
Change the function definition to the following:
void CalcBlockMeanVariance(const cv::Mat &Img, cv::Mat &Res, float blockSide=21)
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?
I'm trying to use the OpenCV Threshold with the depthImage retrieved by the OpenCV VideoCapture module, but I get the following error:
OpenCV Error: Bad argument in unknown function,
file PATHTOOPENCV\opencv\modules\core\src\matrix.cpp line 646
My code is as follows:
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/gpu/gpu.hpp"
cv::VideoCapture kinect;
cv::Mat rgbMap;
cv::Mat dispMap;
bool newFrame;
void setup()
{
kinect.open(CV_CAP_OPENNI);
newFrame = false;
}
void update()
{
if(kinect.grab())
{
kinect.retrieve( rgbMap, CV_CAP_OPENNI_BGR_IMAGE);
kinect.retrieve( dispMap, CV_CAP_OPENNI_DISPARITY_MAP );
newFrame = true;
}
}
void draw()
{
if(newFrame)
{
cv::Mat * _thresSrc = new cv::Mat(dispMap);
cv::Mat * _thresDst = new cv::Mat(dispMap);
cvThreshold(_thresSrc, _thresDst, 24, 255, CV_THRESH_BINARY);
// Draw _thresDst;
delete _thresSrc;
delete _thresDst;
newFrame = false;
}
}
Thank you very much for your help
To start things off, you are mixing the C interface with the C++ interface, and they shouldn't be mixed together!
cv::Mat belongs to the C++ interface, and cvThreshold() belongs to the C. You should use cv::threshold() instead:
double cv::threshold(const Mat& src, Mat& dst, double thresh, double maxVal, int thresholdType)
Parameters:
src – Source array (single-channel, 8-bit of 32-bit floating point)
dst – Destination array; will have the same size and the same type as src
thresh – Threshold value
maxVal – Maximum value to use with THRESH_BINARY and THRESH_BINARY_INV thresholding types
thresholdType – Thresholding type (see the discussion)