Does opencv have a constexpr vector or matrix container? - c++

I would like to do something like this for example:
constexpr std::array<cv::Vec3b, 6> COLORS {
cv::Vec3b(0, 255, 0), // green
cv::Vec3b(255, 0, 0), // blue
cv::Vec3b(0, 0, 255), // red
cv::Vec3b(0, 255, 255), // yellow
cv::Vec3b(255, 0, 255), // magenta
cv::Vec3b(255, 255, 0) // cyan
}
However, cv::Vec3b cannot be made constexpr, is there support for constexpr containers in opencv? Or another way around this?
Compiler error:
error: the type ‘const std::vector<cv::Vec<unsigned char, 3> >’ of ‘constexpr’ variable ‘COLORS’ is not literal
};
Thank you

Related

About opencv approxPolyDP function. Program can't find rectangle that I want if I change the angle

// I used cvtColor, inRange , bitwise_and, GaussianBlur, Canny functions before.
vector<vector<Point>> contours; // to store contours
vector<Vec4i> hierarchy;
int mode = RETR_CCOMP;
//int module = CHAIN_APPROX_NONE;
//int module = CHAIN_APPROX_SIMPLE;
//int module = CHAIN_APPROX_TC89_L1;
int module = CHAIN_APPROX_TC89_KCOS;
findContours(img_edge, contours, hierarchy, mode, module);
sort(contours.begin(), contours.end(), compareContourAreas);
drawContours(image, contours, -1, Scalar(255, 0, 0), 1);
imshow("ContourImage", image);
vector<Point> contours_approx;
for (int i = 0; i < 5; i++)
{
double length = arcLength(contours[i], true);
approxPolyDP(contours[i], contours_approx, 0.1 * length, true);
if (contours_approx.size() == 4)
{
break;
}
contours_approx.clear();
}
for (Point i : contours_approx)
{
cout << i << endl; // to check coordinate of vertex
}
drawContours(contour2, vector<vector<Point>>(1, contours_approx), -1, Scalar(0, 0, 255), 1);
// to find vertex
circle(contour2, Point(x0, y0), 5, Scalar(255, 0, 0), -1, -1, 0); // blue
circle(contour2, Point(x1, y1), 5, Scalar(0, 255, 0), -1, -1, 0); // green
circle(contour2, Point(x2, y2), 5, Scalar(0, 0, 255), -1, -1, 0); // red
circle(contour2, Point(x3, y3), 5, Scalar(255, 255, 255), -1, -1, 0); // white
imshow("contour2", contour2);
In first, second picture, there is a result that I want. There are circles at vertex of rectangle.
But in third, fourth picture,I modified the angle of cuboid, and there is a result that I don't want. Circles were in very small rectangle above the large rectangle.
In first, third picture I guess that findContours function works well. And I guess that there is a problem in approxPolyDP function.
To locate circle in the largest rectangle, what should I have to do?
Thanks for your answer

Translucent objects on IplImage

I draw objects on IplImage like this:
cvLine(image, point_1, point_2, color, thickness, CV_AA); // Line
cvCircle(mage, point, radius, color, thickness, CV_AA); // Circle
// and some others...
How can I draw them translucent? cv::Scalar does not support alpha channel, if I understand correctly. I found something similar, but not quite appropriate: link. Here we are talking about translucenty IplImage, not about the objects on it.
So, I tested it now with IplImage and cv::Mat, and both cvCircle and cv::circle don't support drawing semi-transparent objects. I used OpenCV 3.4.0, since this version still supports the old C API.
Let's have a look at the following code:
// IplImage - doesn't work
IplImage* ipl = cvCreateImage(cvSize(201, 201), IPL_DEPTH_8U, 4);
cvSet(ipl, CvScalar(255, 0, 0, 255));
cvCircle(ipl, CvPoint(100, 100), 50, CvScalar(0, 0, 255, 128), CV_FILLED);
// cv::Mat - doesn't work
cv::Mat img = cv::Mat(201, 201, CV_8UC4, cv::Scalar(255, 0, 0, 255));
cv::circle(img, cv::Point(100, 100), 50, cv::Scalar(0, 0, 255, 128), cv::FILLED);
We create a blue 4-channel image with zero transparency, and draw a red circle with 0.5 transparency. In both cases, we get the following output:
We see, that the part of red circle actually "replaces" the pixel values in the original blue image.
So, for IplImage as well as for cv::Mat we need to use blending, e.g. using addWeighted. Let's have a look at this code:
// IplImage - works
IplImage* iplBG = cvCreateImage(cvSize(201, 201), IPL_DEPTH_8U, 3);
cvSet(iplBG, CvScalar(255, 0, 0));
IplImage* iplFG = cvCreateImage(cvSize(201, 201), IPL_DEPTH_8U, 3);
cvSet(iplFG, CvScalar(0, 0, 0));
cvCircle(iplFG, CvPoint(100, 100), 50, CvScalar(0, 0, 255), CV_FILLED);
IplImage* iplOut = cvCreateImage(cvSize(201, 201), IPL_DEPTH_8U, 3);
cvAddWeighted(iplBG, 1, iplFG, 0.5, 0, iplOut);
// cv::Mat - works
cv::Mat imgBG = cv::Mat(201, 201, CV_8UC3, cv::Scalar(255, 0, 0));
cv::Mat imgFG = cv::Mat(201, 201, CV_8UC3, cv::Scalar(0, 0, 0));
cv::circle(imgFG, cv::Point(100, 100), 50, cv::Scalar(0, 0, 255), cv::FILLED);
cv::Mat imgOut;
cv::addWeighted(imgBG, 1, imgFG, 0.5, 0, imgOut);
In fact, we create a blue 3-channel background image like this:
And, we create a black foreground 3-channel image of the same size with the red circle:
Using addWeighted with alpha = 1 and beta = 0.5, we get the expected output for both versions:

Draw rotated bounding box on image without fixed angle

Im new to programming and opencv and i try to detect a hdd using color segmentation. My code so far loads an image, creates 3 masks with different colors and draws an upright bounding box around the non zero points:
int main( int argc, char** argv )
{
//Load the image
Mat img = imread(argv[1], 1);
if (img.empty()){
cout << "No image found..." << endl;
return -1;
}
//Extracting colors - BGR
Mat silver, white, black;
//Silver
inRange(img, Scalar(180, 180, 180), Scalar(200, 200, 200), silver);
//White
inRange(img, Scalar(240, 240, 240), Scalar(255, 255, 255), white);
//Black
inRange(img, Scalar(0, 0, 0), Scalar(30, 30, 30), black);
// logical OR mask
Mat1b mask = silver | white | black;
// Find non zero pixels
vector<Point> pts;
findNonZero(mask, pts);
cout << "Non-Zero Locations = " << pts << endl << endl; // get non zero coordinates
// Compute bounding box
Rect box = boundingRect(pts);
// Show bounding box
rectangle(img, box, Scalar(0, 0, 255), 3);
namedWindow("box", CV_WINDOW_NORMAL);
imshow("box", img);
imshow("mask", mask);
waitKey(0);
destroyAllWindows;
return 0;}
Now I want to draw the smallest bounding box, so I tried to use
cv::RotatedRect box2 = cv::minAreaRect(pts);
instead. But it doesnt compile when I try to visualize that by replacing
Rect box = boundingRect(pts);
with
RotatedRect box2 = minAreaRect(pts);
Error Output:
error: no matching function for call to ‘rectangle(cv::Mat&, cv::RotatedRect&, cv::Scalar, int)’
rectangle(img, box2, Scalar(0, 0, 255), 3);
As per the cv::Rectangle Opencv Docs, the function has only 2 variants:
void rectangle(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0)
void rectangle(Mat& img, Rect rec, const Scalar& color, int thickness=1, int lineType=8, int shift=0 )
So it is clear that it only accepts either cv::Rect or cv::Point. Hence there is no provision to directly input the cv::RotatedRect, due to which you are getting the above mentioned error.
To fix this issue, you can extract the 4 points of cv::RotatedRect using:
cv::Point2f points[4];
rotatedRect.points(points);
And then use cv::line() to draw the edges in pairs as:
cv::RotatedRect rotatedRect = cv::RotatedRect(cv::Point(70, 70), cv::Size(90, 90), 30);
cv::Mat canvas = cv::Mat(200, 200, CV_8UC3, cv::Scalar(255, 255, 255));
cv::Point2f points[4];
rotatedRect.points(points);
cv::line(canvas, points[0], points[1], cv::Scalar(0, 255, 0), 3);
cv::line(canvas, points[1], points[2], cv::Scalar(0, 255, 0), 3);
cv::line(canvas, points[2], points[3], cv::Scalar(0, 255, 0), 3);
cv::line(canvas, points[3], points[0], cv::Scalar(0, 255, 0), 3);

OpenCv overlay multiple Mat partly using masks

Hi following up on this brillant way of copying polygons to an other Mat in OpenCv overlay two Mat (drawings not images) with transparency I need to advanced this a bit.
Besides just copying Mats with polygons, I need to copy these polygons ONTO a image. Below you can find my MWE of this.
Following the above mentioned answer I load an additional image called bg in this case (as well 100x100px). What I believed is using inRange again on the result gives me the mask of all polygons (which is working). And than using copyTo to copy all that is in the mask. It copies the pixels from the "background image", but colouring the polygons in black insted of its regular colours.
Any ideas what I forgot to consider in this case?
PS: drawing the polygons directly onto the image is not possible. The code below is just a MWE and in the code below I left out an loop for readability reasons.
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main(int argc, char **argv)
{
cv::Mat bg = imread("bg.png");
cv::Mat m1(100, 100, CV_8UC4, cv::Scalar(0, 0, 0, 0));
cv::Mat m2(m1);
cv::Mat m3(m1);
cv::Mat m4(m1);
cv::polylines(m1, std::vector<cv::Point>{cv::Point{ 100, 20 }, cv::Point{ 0, 40 }}, true, cv::Scalar(6, 6, 255, 255));
cv::polylines(m2, std::vector<cv::Point>{cv::Point{ 100, 100 }, cv::Point{ 0, 0 }}, true, cv::Scalar(192, 112, 0, 255));
cv::polylines(m3, std::vector<cv::Point>{cv::Point{ 0, 50 }, cv::Point{ 100, 50 }}, true, cv::Scalar(0, 228, 0, 255));
cv::polylines(m4, std::vector<cv::Point>{cv::Point{ 20, 0 }, cv::Point{ 20, 100 }}, true, cv::Scalar(0, 228, 255, 0 ));
cv::Mat mask;
cv::Mat result(m1.clone());
cv::inRange(m2, cv::Scalar(0, 0, 0, 0), cv::Scalar(0, 0, 0, 0), mask);
mask = 255 - mask; // invert the mask
m2.copyTo(result, mask);
cv::inRange(result, cv::Scalar(0, 0, 0, 0), cv::Scalar(0, 0, 0, 0), mask);
m3.copyTo(result, mask);
cv::inRange(result, cv::Scalar(0, 0, 0, 0), cv::Scalar(0, 0, 0, 0), mask);
m4.copyTo(result, mask);
cv::inRange(result, cv::Scalar(0, 0, 0, 0), cv::Scalar(0, 0, 0, 0), mask);
bg.copyTo(result, mask);
namedWindow("result", cv::WINDOW_AUTOSIZE);
imshow("result", result);
}

QList initialization via initializer list

My Hard drive got corrupted and I had to set everything up again.
The Problem is that previously working code is now throwing errors.
QList<QColor> colors = {
QColor(0, 255, 255, 255),
QColor(0, 200, 255, 255),
QColor(0, 170, 255, 255),
QColor(0, 150, 255, 255),
QColor(0, 130, 255, 255),
};
Error:
D:\dev\est_tsd\tests\testgis.cpp:19: error: C2440: ‘initializing’: cannot convert from 'initializer-list' to 'QList'
No constructor could take the source type, or constructor overload resolution was ambiguous
I read that Qt supports initializer list with QList now
(Name of the kit: Desktop Qt 5.3 MSVC2013 OpenGL 64bit). What am I missing?
Help would be much appreciated.
You are using copy initialization semantic instead of direct list initialization. You should check if you have in .pro file:
CONFIG += c++11
and then use:
QList<QColor> colors{
QColor(0, 255, 255, 255),
QColor(0, 200, 255, 255),
QColor(0, 170, 255, 255),
QColor(0, 150, 255, 255),
QColor(0, 130, 255, 255)
};
Try removing the last comma.
It becomes:
QList<QColor> colors = {
QColor(0, 255, 255, 255),
QColor(0, 200, 255, 255),
QColor(0, 170, 255, 255),
QColor(0, 150, 255, 255),
QColor(0, 130, 255, 255)};