Inconsistent results for nppi and cv::cvtColor - c++

When I run nppi and cv::cvtColor for color conversion, I get different results.
// *data_ptr = 1, 1, 1, 1
cv::Mat image1(1, 2, CV_8UC2, data_ptr, 2*2);
cv::Mat image2;
cv::cvtColor(image1, image2, cv::COLOR_YUV2RGB_UYVY);
NppiSize nppSize{2, 1};
nppiYUV422ToRGB_8u_C2C3R(
(Npp8u*)data_ptr, 2*2, (Npp8u*)dst_data_ptr, 2*3, nppSize
)
// ------------ Results ------------
// opencv: 0, 153, 0, 0, 153, 0
// nppi: 0, 124, 0, 0, 124, 0
Does anyone know what's going on?

There are several YUV sub-formats.
Have you tried cv::COLOR_YUV2RGB_I420, cv::COLOR_YUV420p2BGR etc. ?
One of them might give you result more similar to nppiYUV422ToRGB_8u_C2C3R.

Related

Creating Your Own Linear Filter

I am new to Opencv C++. I am trying to convolve a mask with an image. For this I want to create my own mask so that I can use the filter2D array function to convolve my mask with the image. The mask which I want to create is:
char mask [3][3]= {{-1,0,1},{-1,0,1},{-1,0,1}};
For this I have tried the code below(to generate this mask):-
Mat kernel(3,3, CV_8UC1, Scalar(-1,0,1));
i have printed the mask values as
std::cout << kernel;
but the answer which I am getting is
0, 0, 0;
0, 0, 0;
0, 0, 0
I am expecting the answer to be
-1, 0, 1;
-1, 0, 1;
-1, 0, 1
I know I am making a mistake in writing the channels properly. Can anyone help me understand what does the channel(CV_8UC1.... ) means and how to correct it and get the proper output.
CV_8UC1 means 1 channel, 8 bit, uchar image.
Scalar is used to set the value of each channel, not each pixel/coordinate.
Ex 1:
Mat kernel(3,3, CV_8UC1, Scalar::all(0))
would mean creating a 3X3 matrix with 0s and since it is of type CV_8UC1, you can mention only one value, in this case 0.
EX 2:
Mat kernel(3,3, CV_8UC3, Scalar(0,0,255))
means creating a 3X3 matrix with 3 channels since the type is CV_8UC3 and setting channel 1 to 0, channel 2 to 0, channel 3 to 255.
Hence for your purpose to set row values, you cannot use scalar.
Instead do this:
Mat C = (Mat_<double>(3,3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);
Check this for more information.
Hope this helps!
You want to create a kernel with negative element for filter2D, then you should't use the data type of CV_8UC1. There is no negative value in unsigned type.
And Mat kernel(3,3, CV_8UC1, Scalar(-1,0,1)); means create a signal- channel-unsigned-char kernel. You set Scalar(-1,0,1) to kernel, then only the first element(that is double -1) is used, while saturate_cast<unchar>(-1) = 0.
Generally, use CV_32FC1 instead.
For example:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main(){
float mask[9] = {-1,0,1, -1, 0, 1, -1,0,1};
Mat kernel(3,3, CV_32FC1);
memcpy(kernel.data, mask, sizeof(float)*9);
cout << kernel<<endl;
}
The result:
[-1, 0, 1;
-1, 0, 1;
-1, 0, 1]
A similar question:
How to create cv::Mat from buffer (array of T* data) using a template function?

HSV color detection with OpenCV

The "red" color-detection is not working yet. The following code is supposed to detect a red bar from an input-image and return a mask-image showing a white bar at the corresponding location.
The corresponding HSV-values of the "red" bar in the inputRGBimage are : H = 177, S = 252, V = 244
cv::Mat findColor(cv::Mat inputRGBimage) {
cv::Mat imageHSV(inputRGBimage.rows, inputRGBimage.cols, CV_8UC3);
cv::Mat imgThreshold(inputRGBimage.rows, inputRGBimage.cols, CV_8UC1);
// convert input-image to HSV-image
cvtColor(inputRGBimage, imageHSV, cv::COLOR_BGR2HSV);
// for red: (H < 14)
// cv::inRange(imageHSV, cv::Scalar(0, 53, 185, 0), cv::Scalar(14, 255, 255, 0), imgThreshold);
// or (H > 165) (...closing HSV-circle)
cv::inRange(imageHSV, cv::Scalar(165, 53, 185, 0), cv::Scalar(180, 255, 255, 0), imgThreshold);
return imgThreshold;
}
The two images below show the inputRGBimage (top) and the returned imgThreshold (bottom). As you can see, the mask is not showing the white bar at the expected color "red" but shows it for some unknown reason at the "blue" bar. Why ????
The following change of the cv::inRange line of code (i.e. H > 120) and its result again illustrates that the color detection is not actually acting as expected :
// or (H > 120) (...closing HSV-circle)
cv::inRange(imageHSV, cv::Scalar(120, 53, 185, 0), cv::Scalar(180, 255, 255, 0), imgThreshold);
As a third example: (H > 100):
// or (H > 100) (...closing HSV-circle)
cv::inRange(imageHSV, cv::Scalar(100, 53, 185, 0), cv::Scalar(180, 255, 255, 0), imgThreshold);
Why the unexpected order of colors in my 3 code-examples (decreasing the H-value from 165 to 100) showing mask orders of "blue->violet->red->orange" instead of the actually expected HSV-wheel rough order of "red->violet->blue->green->yellow->orange" ?????
HSV in OpenCV has ranges:
0 <= H <= 180,
0 <= S <= 255,
0 <= V <= 255, (not quite like in the illustrating graphic above - but the order of colors should be the same for OpenCV HSV-colors - or not ???)
Make sure that the image uses the channel order B, G, R. Also, for the color red you need check two ranges of values, one around H=0 and the other around H=180. You could try this function:
cv::Mat findColor(const cv::Mat & inputBGRimage, int rng=15)
{
// Make sure that your input image uses the channel order B, G, R (check not implemented).
cv::Mat input = inputBGRimage.clone();
cv::Mat imageHSV;//(input.rows, input.cols, CV_8UC3);
cv::Mat imgThreshold, imgThreshold0, imgThreshold1;//(input.rows, input.cols, CV_8UC1);
assert( ! input.empty() );
// convert input-image to HSV-image
cv::cvtColor( input, imageHSV, cv::COLOR_BGR2HSV );
// In the HSV-color space the color 'red' is located around the H-value 0 and also around the
// H-value 180. That is why you need to threshold your image twice and the combine the results.
cv::inRange(imageHSV, cv::Scalar( 0, 53, 185, 0), cv::Scalar(rng, 255, 255, 0), imgThreshold0);
if ( rng > 0 )
{
cv::inRange(imageHSV, cv::Scalar(180-rng, 53, 185, 0), cv::Scalar(180, 255, 255, 0), imgThreshold1);
cv::bitwise_or( imgThreshold0, imgThreshold1, imgThreshold );
}
else
{
imgThreshold = imgThreshold0;
}
return imgThreshold;
}
Good luck! :)

opencv houghcircles differences c c++

I'm introducing myself in OpenCV (in order for an software project at university) and found a tutorial for color circle detection which I adapted and tested. It was written with OpenCV 1 in C. So I tried to convert it to OpenCv 2 classes API and everything was fine, but I ran into one problem:
The C function cvHoughCircles produces other results than the C++ function HoughCircles.
The C version finds my test circle and has a low rate of false positives, but the C++ version has a significantly higher mistake rate.
//My C implementation
IplImage *img = cvQueryFrame( capture );
CvSize size = cvGetSize(img);
IplImage *hsv = cvCreateImage(size, IPL_DEPTH_8U, 3);
cvCvtColor(img, hsv, CV_BGR2HSV);
CvMat *mask = cvCreateMat(size.height, size.width, CV_8UC1);
cvInRangeS(hsv, cvScalar(107, 61, 0, 0), cvScalar(134, 255, 255, 0), mask);
/* Copy mask into a grayscale image */
IplImage *hough_in = cvCreateImage(size, 8, 1);
cvCopy(mask, hough_in, NULL);
cvSmooth(hough_in, hough_in, CV_GAUSSIAN, 15, 15, 0, 0);
cvShowImage("mask",hough_in);
/* Run the Hough function */
CvMemStorage *storage = cvCreateMemStorage(0);
CvSeq *circles = cvHoughCircles(hough_in, storage, CV_HOUGH_GRADIENT,
4, size.height/4, 100, 40, 0, 0);
// ... iterating over all found circles
this works pretty well
//My C++ implementation
cv::Mat img;
cap.read(img);
cv::Size size(img.cols,img.rows);
cv::Mat hsv(size, IPL_DEPTH_8U, 3);
cv::cvtColor(img, hsv, CV_BGR2HSV);
cv::Mat mask(size.height, size.width, CV_8UC1);
cv::inRange(hsv, cv::Scalar(107, 61, 0, 0), cv::Scalar(134, 255, 255, 0), mask);
GaussianBlur( mask, mask, cv::Size(15, 15), 0, 0 );
/* Run the Hough function */
imshow("mask",mask);
vector<cv::Vec3f> circles;
cv::HoughCircles(mask, circles, CV_HOUGH_GRADIENT,
4, size.height/4, 100, 140, 0, 0);
// ... iterating over all found circles
As you can see, I use same arguments to all calls. I tested this with a webcam and a static sample object.One requirement is to use OpenCV2 C++ API.
Does anybody know, why I get so different results under equivalent conditions?
Edit
The different threshold values was just a mistake when I tested to make results more equally. These screenshots are taken with threshold set to 40 for both versions:
Screenshots: (Sorry, cannot yet post images)
C and C++ version
I see Hough parameters in C version as "..., 100, 40, 0, 0); " while in C++ version as "... 100, 140, 0, 0);" This difference in thresholds probably explains the difference in results.

How to use erode and dilate function in opencv?

I'm trying to eliminate the thing around the number with erode and dilate process. I tryed but nothing happened. I changed the values just for see if would change something, but again, nothing has changed. The image continues like in the link above. What about this parameters... I read the documentation but don't quite understand (as you can see, I was guessing in the function). What am I doing wrong?
the image: https://docs.google.com/file/d/0BzUNc6BOkYrNeVhYUk1oQjFSQTQ/edit?usp=sharing
the code:
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
int main ( int argc, char **argv )
{
Mat im_gray;
Mat img_bw;
Mat img_final;
Mat im_rgb = imread("cam.jpg");
cvtColor(im_rgb,im_gray,CV_RGB2GRAY);
adaptiveThreshold(im_gray, img_bw, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV, 105, 1);
dilate(img_bw, img_final, 0, Point(-1, -1), 2, 1, 1);
imwrite("cam_final.jpg", img_final);
return 0;
}
According to official docs, the third argument should be the kernel (or structuring element). You are currently passing 0:
dilate(img_bw, img_final, 0, Point(-1, -1), 2, 1, 1);
Try rewriting it this way:
dilate(img_bw, img_final, Mat(), Point(-1, -1), 2, 1, 1);
In this case, a default 3x3 kernel will be used.
Kernel is basically a matrix. This is multiplied or overlapped on the input matrix(image) to produce the desired output modified(in this case dilated) matrix(image).
Try changing the parameters of Mat() in
dilate(img_bw, img_final, Mat(), Point(-1, -1), 2, 1, 1);
you're basically changing the number of pixels (height and width) of the kernel, which will change the dilation effect on the original pic.
So in the parameters of dilate you use Mat() instead of a number as already stated by esenti.

Scan picture and detect lines

I've got an assignment as follow: "Using cvFilter2D function with the suitable kernels in order to scan a picture and then only hold lines that are +- 45 degree and +- 60 degree".
Can someone give me some clues, especially how to calculate the kernels?
You need a little bit of precalculus.
I assume you want to create a line kernel. So you need to know how to create a line.
http://courses.engr.illinois.edu/ece390/archive/archive-f2000/mp/mp4/anti.html holds plenty of techniques.
In the end, sum over all pixels in the kernel, and normalize them so they add up to 1.
sorry for my late response ! i've done this assignment ! First, thank you again, Perfanoff ! thanks to your reference link, i've found out the way to solve problem !
Here is my code:
// Image Transforms.cpp : Defines the entry point for the console application.
/*The purpose of this program is to detect lines which are +-45 degree and +- 60 degree from a binary picture.*/
#include "stdafx.h"
#include "cv.h"
#include "highgui.h"
int _tmain(int argc, _TCHAR* argv[])
{
//IplImage* src = cvLoadImage("C:\\Users\\USER\\Desktop\\black white 1.jpg");
IplImage* src = cvLoadImage("C:\\Users\\USER\\Desktop\\line detection 4.png");
cvNamedWindow("src", CV_WINDOW_NORMAL);
cvShowImage("src", src);
IplImage* DstSum = cvCreateImage(cvGetSize(src),src->depth, 3);
IplImage* Dst45 = cvCreateImage(cvGetSize(src),src->depth, 3);
IplImage* Dst135 = cvCreateImage(cvGetSize(src),src->depth, 3);
IplImage* Dst60 = cvCreateImage(cvGetSize(src),src->depth, 3);
IplImage* Dst120 = cvCreateImage(cvGetSize(src),src->depth, 3);
/*double Ker0 [] = { -0.1,-0.1,-0.1,-0.1,-0.1,
0, 0, 0, 0, 0, 0,
0.2,0.2,0.2,0.2,0.2,
0, 0, 0, 0, 0, 0,
-0.1,-0.1,-0.1,-0.1,-0.1
};
double Ker90 [] = {-0.1,0,0.2,0,-0.1,
-0.1,0,0.2,0,-0.1,
-0.1,0,0.2,0,-0.1,
-0.1,0,0.2,0,-0.1,
-0.1,0,0.2,0,-0.1
}; */
double Ker45[]={
0,-0.1,-0.1, 0, 0.2,
-0.1,-0.1, 0, 0.2, 0,
-0.1, 0, 0.2, 0,-0.1,
0, 0.2, 0,-0.1,-0.1,
0.2, 0,-0.1,-0.1, 0
};// 45 degree
CvMat Kernel45=cvMat(5, 5, CV_64FC1,Ker45);
double Ker135[]={
0.2, 0,-0.1,-0.1, 0,
0, 0.2, 0,-0.1,-0.1,
-0.1, 0, 0.2, 0,-0.1,
-0.1,-0.1, 0, 0.2, 0,
0,-0.1,-0.1, 0, 0.2
};// 135 degree
CvMat Kernel135=cvMat(5, 5, CV_64FC1,Ker135);
double Ker120[] = {0,0,0,0,0,0,0,
1/7,0.25/7,0,0,0,0,0,
0,0.75/7,0.75/7,0.25/7,0,0,0,
0,0,0,0.75/7,0.6/7,0.25/7,0,
0,0,0,0,0.4/7,0.75/7,1/7,
0,0,0,0,0,0,0,
0,0,0,0,0,0,0
};//120 degree
CvMat Kernel120=cvMat(7, 7, CV_64FC1,Ker120);
double Ker60[] = {0,0,0,0,1/7,0,0,
0,0,0,0.25/7,0.75/7,0,0,
0,0,0,0.6/7,0.4/7,0,0,
0,0,0.25/7,0.75/7,0,0,0,
0,0,0.75/7,0.25/7,0,0,0,
0,0.25/7,0.75/7,0,0,0,0,
0,1/7,0,0,0,0,0
};//60 degree
CvMat Kernel60=cvMat(7, 7, CV_64FC1,Ker60);
cvFilter2D(src,Dst60,&Kernel60,cvPoint(-1,-1));
cvThreshold(Dst60,Dst60,100,255,CV_THRESH_BINARY);
cvFilter2D(src,Dst120,&Kernel120,cvPoint(-1,-1));
cvThreshold(Dst120,Dst120,100,255,CV_THRESH_BINARY);
cvFilter2D(src,Dst45,&Kernel45,cvPoint(-1,-1));
cvThreshold(Dst45,Dst45,200,255,CV_THRESH_BINARY);
cvFilter2D(src,Dst135,&Kernel135,cvPoint(-1,-1));
cvThreshold(Dst135,Dst135,200,255,CV_THRESH_BINARY);
cvAdd(Dst45,Dst60,DstSum,NULL);
cvAdd(Dst135,DstSum,DstSum,NULL);
cvAdd(Dst120,DstSum,DstSum,NULL);
cvNamedWindow("dst", CV_WINDOW_NORMAL);
cvShowImage("dst", DstSum);
cvReleaseImage(&DstSum);
cvWaitKey(0);
cvReleaseImage(&src);
cvDestroyWindow("src");
cvDestroyWindow("dst");
return 0;
}
Here is the result: