Setting up depth for a cv::Mat - c++

I want to test a function that is looking for specific mat depth && number of channels
It has a test...
if (image.channels() == 1 && image.depth() == 8) ...
else if (image.channels() == 1 && image.depth() == 16) ...
else if (image.channels() == 1 && image.depth() == 32) ...
else
{
if ((image.channels() != 3) || (image.depth() != 8))
{printf("Expecting rgb24 input image"); return false;}
...
}
I prefer to test with a made-up mat, to avoid using local resources:
cv::Mat M(255, 255, CV_8UC3, cv::Scalar(0,0,255));
printf("M: %d %d \n", M.channels(), M.depth());
cv::Mat M1(255, 255, CV_32F, cv::Scalar(0,0,255));
cv::Mat M2(255, 255, CV_32FC3, cv::Scalar(0,0,255));
cv::Mat M2(255, 255, CV_8SC3, cv::Scalar(0,0,255));
I have experimented with all kinds of combinations, but if I print, I get 0 depth.
I have also tried to load a png or a jpg file - with same result (I prefer not to use outside files... but I see no reason why this doesn't work)
cv::Mat M3 = cv::imread( "c:/my_image.png", CV_LOAD_IMAGE_COLOR );
cv::Mat M3 = cv::imread( "c:/my_image.jpg", CV_LOAD_IMAGE_COLOR );
They all seem to have depth = 0 ?
Is there something else I have to do ? I can't see anything in documentation.

When you call depth() on Mat, it returns depth values as defined below instead of number of bits:
#define CV_8U 0
#define CV_8S 1
#define CV_16U 2
#define CV_16S 3
#define CV_32S 4
#define CV_32F 5
#define CV_64F 6
And you can use cv::DataDepth::value to figure out which one is which. For example,
cv::DataDepth<unsigned char>::value == CV_8U;
cv::DataDepth<float>::value == CV_32F;
So you should get 0 on all CV_8UCX matrix, and when you load an image, it is usually loaded as CV_8UC3, so you will get 0 as well. But I am not sure why you got 0 on cv::Mat M(255, 255, CV_32FC3), I tested it on my computer, it returned 5.

Related

I'm getting an error from the implementation of my region of interest

Error message:
(-215) 0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols &&
0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows in
function cv::Mat::Mat
This is my code
Rect eye_rec(200, 300, 168, 168);
Point hand_pos(100, 100);
Mat des, mask = (cv::Mat::zeros(hand.size(), CV_8UC1));
mask(eye_rec).setTo(255);
seamlessClone(eye,hand, mask,hand_pos,des,NORMAL_CLONE);
imshow("clone", des);
waitKey(0);
i cant really understand the error message though..
Your error code generally means, that ROI you want to crop is out of the bounds of the source matrix - e.g. source matrix is of size 480x480 and you want to crop out ROI of size 300x300 from position (200, 200), where 300+200 > 480.
According to docs
src – Input 8-bit 3-channel image.
dst – Input 8-bit 3-channel image.
mask – Input 8-bit 1 or 3-channel image.
result – Output image with the same size and type as dst.
src, dst and result should be of type CV_8UC3 - three channel images, while you are passing just one channel images CV_8UC1, which most likely cause the error here.
The solution is to use 3-channel (color) images or different operation accepting 1-channel images.
hand.convertTo(hand, CV_8UC3);
eye.convertTo(eye, CV_8UC3);
Point hand_pos(hand.cols/2,hand.rows/2); //this code should put the eye image in the middle of the hand image
Mat des, mask = (cv::Mat::zeros(eye.size(), CV_8UC3));
des.convertTo(des, CV_8UC3);
mask = 255 * Mat::ones(eye.rows, eye.cols, eye.depth()); // creating a mask of all white from the eye image
seamlessClone(eye,hand, mask,hand_pos,des,NORMAL_CLONE);
imshow("normalclone", des); waitKey(0);
seamlessClone(eye,hand,mask,hand_pos,des, MIXED_CLONE);
imshow("mixclone",des); waitKey(0)
waitKey(0);
This change helped me, hope it helps others too, thanks #Filip Kočica

Error when converting from HSV to BGR, or HSV to JPEG in openCV after using inRange

I am using openCV 3.1.0 (I have tried with 2.4.9, with same problem). I want to output a HSV mat to jpeg:
// .. Getting JPEG content into memory
// JPEG to mat
Mat imgBuf=Mat(1, jpegContent, CV_8UC3, jpegSize);
Mat imgMat=imdecode(imgBuf, CV_LOAD_IMAGE_COLOR);
free(jpegContent);
if(imgMat.data == NULL) {
// Some error handling
}
// Now the JPEG is decoded and reside in imgMat
cvtColor(imgMat, imgMat, CV_BGR2HSV); // Converting to HSV
Mat tmp;
inRange(imgMat, Scalar(0, 0, 0), Scalar(8, 8, 8), tmp); // Problem goes here
cvtColor(tmp, imgMat, CV_HSV2BGR);
// Mat to JPEG
vector<uchar> buf;
imencode(".jpg", imgMat, buf, std::vector<int>());
outputJPEG=(unsigned char*)malloc(buf.size());
memcpy(outputJPEG, &buf[0], buf.size());
// ... Output JPEG
The problem is, when i do cvtColor(tmp, imgMat, CV_HSV2BGR) with inRange, my program will fail with:
OpenCV Error: Assertion failed (scn == 3 && (dcn == 3 || dcn == 4) && (depth == CV_8U || depth == CV_32F)) in cvtColor, file /home/pi/opencv/src/opencv-3.1.0/modules/imgproc/src/color.cpp, line 8176
terminate called after throwing an instance of 'cv::Exception'
what(): /home/pi/opencv/src/opencv-3.1.0/modules/imgproc/src/color.cpp:8176: error: (-215) scn == 3 && (dcn == 3 || dcn == 4) && (depth == CV_8U || depth == CV_32F) in function cvtColor
If i removed inRange, the program work just fine. I have tried to remove the cvtColor call, letting imencode to do its job, automatically converting HSV to BGR then to JPEG. This time, no more assertion failed, but i got corrupted JPEG image, as GStreamer complains:
gstrtpjpegpay.c(581): gst_rtp_jpeg_pay_read_sof ():
/GstPipeline:pipeline0/GstRtpJPEGPay:rtpjpegpay0
WARNING: from element /GstPipeline:pipeline0/GstRtpJPEGPay:rtpjpegpay0: Wrong SOF length 11.
Again, removing inRange also solves this issue, it produces good JPEG data.
So, is that i am invoking inRange improperly that cause corrupted image data? If yes, what is the correct way to use inRange?
inRange produces a single channel binary matrix, i.e. a CV_8UC1 matrix with values either 0 or 255.
So you cannot convert tmp with HSV2BGR, because the source image tmp doesn't have 3 channels.
OpenCV is telling you exactly this: scn (source channels) is not 3.
Since you probably want to keep and then convert to BGR only part of the image in your range, you can:
set to black everything outside the range: imgMat.setTo(Scalar(0,0,0), ~tmp);
convert the resulting image to BGR: cvtColor(imgMat, imgMat, CV_HSV2BGR);

countNonZero function gives an assertion error in openCV

I tried to get horizontal projection using countNonZero() function as below.
Mat src = imread(INPUT_FILE, CV_LOAD_IMAGE_COLOR);
Mat binaryImage = src.clone();
cvtColor(src, src, CV_BGR2GRAY);
Mat horizontal = Mat::zeros(1,binaryImage.cols, CV_8UC1);
for (int i = 0; i<binaryImage.cols; i++)
{
Mat roi = binaryImage(Rect(0, 0, 1, binaryImage.rows));
horizontal.at<int>(0,i) = countNonZero(roi);
cout << "Col no:" << i << " >>" << horizontal.at<int>(0, i);
}
But an error is occured in the line of calling countonZero() function. Error is as follows.
OpenCV Error: Assertion failed (src.channels() == 1 && func != 0) in cv::countNo
nZero, file C:\builds\2_4_PackSlave-win32-vc12-shared\opencv\modules\core\src\st
at.cpp, line 549
Can somebody please point out the mistake?
Assertion src.channels() == 1 means that image should have 1 channel, i.e. it has to be gray, not colored. You are calling countNonZero on roi, which is a subimage of binaryImage, which is a clone of src, which is originally colored.
I suppose you wanted to write cvtColor(binaryImage, binaryImage, CV_BGR2GRAY);. In this case it makes sense. However, I do not see you using src anywhere again, so perhaps you do not need this intermediate image. In case you do, do not call "binary", since "binary" in computer vision usually stands for black-or-white image, only two colors. Your image is "gray", since it has all shades of black and white.
Concerning your original task, Miki is right, you should use cv::reduce for it. He already gave you an example on how to use it.
BTW, you can compute horizontal projection using reduce giving as argument CV_REDUCE_SUM.
A minimal example:
Mat1b mat(4, 4, uchar(0));
mat(0,0) = uchar(1);
mat(0,1) = uchar(1);
mat(1,1) = uchar(1);
// mat is:
//
// 1100
// 0100
// 0000
// 0000
// Horizontal projection, result would be a column matrix
Mat1i reducedHor;
cv::reduce(mat, reducedHor, 1, CV_REDUCE_SUM);
// reducedHor is:
//
// 2
// 1
// 0
// 0
// Vertical projection, result would be a row matrix
Mat1i reducedVer;
cv::reduce(mat, reducedVer, 0, CV_REDUCE_SUM);
// reducedVer is:
//
// 1200
// Summary
//
// 1100 > 2
// 0100 > 1
// 0000 > 0
// 0000 > 0
//
// vvvv
// 1200
You can use this with your images like this:
// RGB image
Mat3b img = imread("path_to_image");
// Gray image, contains values in [0,255]
Mat1b gray;
cvtColor(img, gray, CV_BGR2GRAY);
// Binary image, contains only 0,1 values
// The sum of pixel values will equal the count of non-zero pixels
Mat1b binary;
threshold(gray, binary, 1, 1, THRESH_BINARY);
// Horizontal projection
Mat1i reducedHor;
cv::reduce(binary, reducedHor, 1, CV_REDUCE_SUM);
// Vertical projection
Mat1i reducedVer;
cv::reduce(binary, reducedVer, 0, CV_REDUCE_SUM);

Assertion failed with accumulateWeighted in OpenCV

I am using openCV and trying to calculate a moving average of the background, then taking the current frame and subtracting the background to determine movement (of some sort).
However, when running the program I get:
OpenCV Error: Assertion failed (func != 0) in accumulateWeighted, file /home/sebbe/projekt/opencv/trunk/opencv/modules/imgproc/src/accum.cpp, line 431
terminate called after throwing an instance of 'cv::Exception'
what(): /home/sebbe/projekt/opencv/trunk/opencv/modules/imgproc/src/accum.cpp:431: error: (-215) func != 0 in function accumulateWeighted
I cant possibly see what arguments are wrong to accumulateWeighted.
Code inserted below:
#include <stdio.h>
#include <stdlib.h>
#include "cv.h"
#include "highgui.h"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "cxcore.h"
using namespace cv;
int main( int argc, char **argv )
{
Mat colourFrame;
Mat frame;
Mat greyFrame;
Mat movingAverage;
Mat difference;
Mat temp;
int key = 0;
VideoCapture cap(0);
/* always check */
if ( !cap.isOpened() ) {
fprintf( stderr, "Cannot open initialize webcam!\n" );
return 1;
}
namedWindow("Camera Window", 0);
// Initialize
cap >> movingAverage;
while( key != 'q' ) {
/* get a frame */
cap >> colourFrame;
/* Create a running average of the motion and convert the scale */
accumulateWeighted(colourFrame, movingAverage, 0.02, Mat() );
/* Take the difference from the current frame to the moving average */
absdiff(colourFrame, movingAverage, difference);
/* Convert the image to grayscale */
cvtColor(difference, greyFrame, CV_BGR2GRAY);
/* Convert the image to black and white */
threshold(greyFrame, greyFrame, 70, 255, CV_THRESH_BINARY);
/* display current frame */
imshow("Camera Window",greyFrame);
/* exit if user press 'q' */
key = cvWaitKey( 1 );
}
return 0;
}
Looking at the OpenCV sources, specifically at modules/imgproc/src/accum.cpp line 431, the lines that precede this assertion are:
void cv::accumulateWeighted( InputArray _src, CV_IN_OUT InputOutputArray _dst,
double alpha, InputArray _mask )
{
Mat src = _src.getMat(), dst = _dst.getMat(), mask = _mask.getMat();
int sdepth = src.depth(), ddepth = dst.depth(), cn = src.channels();
CV_Assert( dst.size == src.size && dst.channels() == cn );
CV_Assert( mask.empty() || (mask.size == src.size && mask.type() == CV_8U) );
intfidx = getAccTabIdx(sdepth, ddepth);
AccWFunc func = fidx >= 0 ? accWTab[fidx] : 0;
CV_Assert( func != 0 ); // line 431
What's happening in your case is that getAccTabIdx() is returning -1, which in turn makes func be ZERO.
For accumulateWeighted() to work properly, the depth of colourFrame and movingAverage must be one of the following options:
colourFrame.depth() == CV_8U && movingAverage.depth() == CV_32F
colourFrame.depth() == CV_8U && movingAverage.depth() == CV_64F
colourFrame.depth() == CV_16U && movingAverage.depth() == CV_32F
colourFrame.depth() == CV_16U && movingAverage.depth() == CV_64F
colourFrame.depth() == CV_32F && movingAverage.depth() == CV_32F
colourFrame.depth() == CV_32F && movingAverage.depth() == CV_64F
colourFrame.depth() == CV_64F && movingAverage.depth() == CV_64F
Anything different than that will make getAccTabIdx() return -1 and trigger the exception at line 431.
From the documentation on OpenCV API you can see that the output image from accumulateWeighted is
dst – Accumulator image with the same number of channels as input image, 32-bit or 64-bit floating-point.
So your initialization is wrong. You should retrieve the colourFrame size first and then do this:
cv::Mat movingAverage = cv::Mat::zeros(colourFrame.size(), CV_32FC3);
On Python a working solution is to initiate movingAverage using FIRSTcolourFrame.copy().astype("float").
I found the solution on this website

OpenCV running kmeans algorithm on an image

I am trying to run kmeans on a 3 channel color image, but every time I try to run the function it seems to crash with the following error:
OpenCV Error: Assertion failed (data.dims <= 2 && type == CV_32F && K > 0) in unknown function, file ..\..\..\OpenCV-2.3.0\modules\core\src\matrix.cpp, line 2271
I've included the code below with some comments to help specify what is being passed in. Any help is greatly appreciated.
// Load in an image
// Depth: 8, Channels: 3
IplImage* iplImage = cvLoadImage("C:/TestImages/rainbox_box.jpg");
// Create a matrix to the image
cv::Mat mImage = cv::Mat(iplImage);
// Create a single channel image to create our labels needed
IplImage* iplLabels = cvCreateImage(cvGetSize(iplImage), iplImage->depth, 1);
// Convert the image to grayscale
cvCvtColor(iplImage, iplLabels, CV_RGB2GRAY);
// Create the matrix for the labels
cv::Mat mLabels = cv::Mat(iplLabels);
// Create the labels
int rows = mLabels.total();
int cols = 1;
cv::Mat list(rows, cols, mLabels .type());
uchar* src;
uchar* dest = list.ptr(0);
for(int i=0; i<mLabels.size().height; i++)
{
src = mLabels.ptr(i);
memcpy(dest, src, mLabels.step);
dest += mLabels.step;
}
list.convertTo(list, CV_32F);
// Run the algorithm
cv::Mat labellist(list.size(), CV_8UC1);
cv::Mat centers(6, 1, mImage.type());
cv::TermCriteria termcrit(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0);
kmeans(mImage, 6, labellist, termcrit, 3, cv::KMEANS_PP_CENTERS, centers);
The error says all: Assertion failed (data.dims <= 2 && type == CV_32F && K > 0)
These are very simple rules to understand, the function will work only if:
mImage.depth() is CV_32F
if mImage.dims is <= 2
and if K > 0. In this case, you define K as 6.
From what you stated on the question, it seems that:
IplImage* iplImage = cvLoadImage("C:/TestImages/rainbox_box.jpg");`
is loading the image as IPL_DEPTH_8U by default and not IPL_DEPTH_32F. This means that mImage is also IPL_DEPTH_8U, which is why your code is not working.