Related
I tried to extract average pixel values (R, G, B) from the contoured image. However, my problem is when I applied the code below, something strange values were observed.
int main(){
cv::Mat star = imread("C:\\Users\\PC\\Desktop\\star\\starcircle.png");
cv::Mat mask = cv::Mat::zeros(star.rows, star.cols, CV_8UC1);
cv::Mat frame;
double b, g, r = 0.0;
cv::imshow("Original", star);
cv::cvtColor(star, frame, CV_BGR2HSV);
cv::inRange(frame, cv::Scalar(29, 220, 220), cv::Scalar(30, 255, 255), mask);
cv::imshow("mask", mask);
cv::Mat result = cv::Mat(star.rows, star.cols, CV_8UC1, star.type());
result.setTo(cv::Scalar(0, 0, 0));
star.copyTo(result, mask);
cv::Scalar temp = mean(mask);
cout << "avg_R: " << temp[2] << " \n"; // red value
cout << "avg_G: " << temp[1] << " \n"; // green value
cout << "avg_B: " << temp[0] << " \n\n"; // blue value
cv::imshow("result", result);
cv::waitKey(-1);
return 0;
}
And I got the correct images for the result like below.
I want to read pixel values only for yellow part, not for outside of mask.
And I have another code for read out pixel values in the yellow parts, but it showed the same result.
int main(){
cv::Mat star = imread("C:\\Users\\PC\\Desktop\\star\\starcircle.png");
cv::Mat mask = cv::Mat::zeros(star.rows, star.cols, CV_8UC1);
cv::Mat frame;
double b, g, r = 0.0;
cv::imshow("Original", star);
cv::cvtColor(star, frame, CV_BGR2HSV);
cv::inRange(frame, cv::Scalar(29, 220, 220), cv::Scalar(30, 255, 255), mask);
cv::imshow("mask", mask);
cv::Mat result = cv::Mat(star.rows, star.cols, CV_8UC1, star.type());
result.setTo(cv::Scalar(0, 0, 0));
star.copyTo(result, mask);
int hei = star.rows;
int wid = star.cols;
int corow = hei * wid;
double b, g, r = 0.0;
for (int x = 0; x < hei; x++) {
for (int y = 0; y < wid; y++) {
if (mask.at<unsigned char>(x, y) > 0) {
b += result.at<Vec3b>(x, y)[0];
g += result.at<Vec3b>(x, y)[1];
r += result.at<Vec3b>(x, y)[2];
}
else {
}
}
}
cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
cout << "avg_R: " << r / corow << " \n"; // red value
cout << "avg_G: " << g / corow << " \n"; // green value
cout << "avg_B: " << b / corow << " \n\n"; // blue value
}
Please help me to revise the error.
Thank you in advance.
A few things:
Your variables names and Mat types are at least confusing. Use proper names for the variables, and use Mat_<T> whenever possible (I'd say always).
To get the mean you should divide by the number of pixels in the mask, not by total number of pixels.
you should consider using cv::mean
you need cv::waitKey() to actually see your cv::imshow
Check the code:
#include <opencv2\opencv.hpp>
int main()
{
cv::Mat3b star = cv::imread("path/to/image");
cv::imshow("Original", star);
cv::Mat3b hsv;
cv::cvtColor(star, hsv, cv::COLOR_BGR2HSV);
cv::Mat1b mask;
cv::inRange(hsv, cv::Scalar(29, 220, 220), cv::Scalar(30, 255, 255), mask);
cv::imshow("mask", mask);
// Change to 'false' to see how to use the 'cv::mask' approach
if (true)
{
double blue, green, red = 0.0;
int counter = 0;
for (int r = 0; r < star.rows; r++)
{
for (int c = 0; c < star.cols; c++)
{
if (mask(r, c) > 0)
{
++counter;
blue += star(r, c)[0];
green += star(r, c)[1];
red += star(r, c)[2];
}
}
}
// Avoid division by 0
if (counter > 0)
{
blue /= counter;
green /= counter;
red /= counter;
}
std::cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
std::cout << "avg_R: " << red << " \n";
std::cout << "avg_G: " << green << " \n";
std::cout << "avg_B: " << blue << " \n\n";
}
else
{
cv::Scalar mean_value = cv::mean(star, mask);
double blue = mean_value[0];
double green = mean_value[1];
double red = mean_value[2];
std::cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
std::cout << "avg_R: " << red << " \n"; // red value
std::cout << "avg_G: " << green << " \n"; // green value
std::cout << "avg_B: " << blue << " \n\n"; // blue value
}
cv::waitKey();
}
I see several errors in your code:
cv::Mat result = cv::Mat(star.rows, star.cols, CV_8UC1, star.type());
result.setTo(cv::Scalar(0, 0, 0));
star.copyTo(result, mask);
cv::Scalar temp = mean(mask);
If result is of type CV_8UC1 then you copyTo one channel? (The C1 from CV_8U means one channel). Then you use star.type() where the value to be set should be... You do also the mean to a mask, which will give you a scalar with only one channel set, since it is a binary image of type CV_8UC1... for it to work, it should be:
cv::Mat result = cv::Mat(star.rows, star.cols, star.type(), cv::Scalar::all(0));
star.copyTo(result, mask);
cv::Scalar temp = mean(result);
For the second part, it is ok to add it like that, however if you have not fixed the previous error... I think it should give you segmentation error at some point or weird results if you are lucky. Finally the result part you have this:
cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
cout << "avg_R: " << r / corow << " \n"; // red value
cout << "avg_G: " << g / corow << " \n"; // green value
cout << "avg_B: " << b / corow << " \n\n"; // blue value
but corow should be the non zero points of the mask, so it should be:
corow = cv::countNonZero(mask);
cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
cout << "avg_R: " << r / corow << " \n"; // red value
cout << "avg_G: " << g / corow << " \n"; // green value
cout << "avg_B: " << b / corow << " \n\n"; // blue value
if not it will give you a smaller number, since it is divided with a a number that includes the black points which do not contribute.
As an extra note, you should use more OpenCV functions... in this case cv::mean does the same thing, if not you can simplify it with sum and divide like:
cv::Scalar summed = cv::sum(result);
cv::Scalar mean = summed / static_cast<double>(cv::countNonZero(mask));
std::cout << "$$ Red(R), Green(G), Blue(B) $$" << std::endl << std::endl;
std::cout << "avg_R: " << mean[2] << std::endl; // red value
std::cout << "avg_G: " << mean[1] << std::endl; // green value
std::cout << "avg_B: " << mean[0] << std::endl << std::endl; // blue value
This is assuming you did the star.copyTo(result, mask); line
Read about cv::Mat::at: first row, second col. Not (x, y)!
See you on cv::mean: it can work with mask.
Right initialization: double b = 0.0, g = 0.0, r = 0.0;
int corow = 0; And inside loop ++corow if mask > 0.
I have encountered on designing program to allow capturing images every second from video files (avi, mp4, etc...).
First, I was able to capture images frame by frame from video file.
Second, I was able to analyze pixel color values from images in the same folder at the same time and saved pixel value in the txt file.
And here I have some problem. I am now trying to combine these two codes at once, but I have strange results. I refer the code below.
int main(){
VideoCapture cap("D:\\data\\extra\\video200ul.avi");
if (!cap.isOpened())
return -1;
Ptr<BackgroundSubtractor> pMOG2 = createBackgroundSubtractorMOG2(20, 16, true);
Mat fg_mask;
Mat frame;
int count = 0;
String name, folder;
for (;;) {
// Get frame
cap >> frame; // get a new frame from video
++count;
// Update counter
// Background subtraction
if (count % 2 == 0) {
pMOG2->apply(frame, fg_mask, 0.001);
cout << count << endl;
if (!frame.empty()) {
imshow("frame", frame);
// imshow("fg_mask", fg_mask);
}
// Save foreground mask
name = "mask" + std::to_string(count) + ".png";
// string name = "mask_" + std::to_string(static_cast<long long>(count) + ".png";
folder = imwrite("D:\\data\\extra\\" + name, frame);
}
anal(folder);
}
waitKey(0);
return 0;
}
First, The code above I wrote is for capturing images frame by frame from video file. However, if I got the images per frame, I will have so many pictures on my folder, so I would like to capture an image per second from the video file. I have tried to use CV_CAP_PROP_POS_MSEC instead using cap << frame, but it did not work for me.
Second, when I merge this code to another code what I wrote below, it showed some error messages like, "libpng warning image width, length, data are zero in ihdr."
int anal(String folder) {
folder = "D:\\data\\extra\\*.png";
vector<String> filenames;
glob(folder, filenames);
cv::Mat ori_image;
for (size_t i = 0; i < filenames.size(); ++i) {
ori_image = imread(filenames[i], IMREAD_COLOR);
if (ori_image.empty()) {
cout << "Check your file again." << std::endl;
return -1;
}
rectangle(ori_image, Point(215, 98), Point(245, 110), Scalar(0, 255, 255), 1);
imshow("Original Image", ori_image);
cv::Scalar sums;
sums = cv::sum(ori_image);
double totalSum = sums[0] + sums[1] + sums[2];
if (totalSum <= 0) {
cout << "$$ RGB percentage $$" << " \n\n";
cout << "R: " << 100.0 / 3 << " % \n";
cout << "G: " << 100.0 / 3 << " % \n";
cout << "B: " << 100.0 / 3 << " % \n\n";
}
else {
cout << "$$ RGB percentage $$" << " \n\n"; // red value
cout << "R: " << sums[2] / totalSum * 100 << " % \n"; // red value
cout << "G: " << sums[1] / totalSum * 100 << " % \n"; // green value
cout << "B: " << sums[0] / totalSum * 100 << " % \n\n"; // blue value
}
}
as I prepared the code above, I tried to calculate red, blue, green percentages of all the captured images from the video. However, when I separate these two code and run them, they worked fine, but if I merge them together, It showed error messages.
I would like to combine these two code for analysis for color values from the captured images at video every second.
Please help me out this problem.
Thank you in advance.
-----------Edited part----------------------
I used your revised version and applied to my updated code,
void imageAnalysis(std::string folder, cv::Mat frame){
cv::Mat ori_image = frame.clone();
std::string path = folder;
cv::rectangle(ori_image, Point(215, 105), Point(245, 120), Scalar(0, 255, 255), 1);
cv::imshow("Original Image", ori_image);
cv::waitKey(1);
String folder = "D:\\data\\dfdf\\*.png";
vector<String> filenames;
cv::glob(path, filenames);
for (size_t t = 0; t < filenames.size(); t++) {
ori_image = imread(filenames[t], IMREAD_COLOR); // ori_image
if (ori_image.empty()) { //ori_image
cout << "Check your file again." << "\n";
break;
//return -1;
}
rectangle(ori_image, Point(215, 105), Point(245, 120), Scalar(0, 255, 255), 1);
imshow("Original Image", ori_image);
cv::waitKey(1);
Mat image_HSV;
cvtColor(ori_image, image_HSV, CV_BGR2HSV);
double h = 0.0;
double s = 0.0;
double v = 0.0;
int col = image_HSV.cols; // 480
int row = image_HSV.rows; // 272
int corow = ((col - 235) - 215) * ((row - 152) - 108);
Mat mask;
inRange(image_HSV, Scalar(100, 0, 0), Scalar(100, 255, 255), mask); // convert binary
image_HSV.setTo(Scalar(0, 0, 0), mask);
for (int i = 108; i < row - 152; i++) {
for (int j = 215; j < col - 235; j++) {
Vec3b hsv = image_HSV.at<cv::Vec3b>(i, j);
h += (int)(hsv.val[0]);
s += (int)(hsv.val[1]);
v += (int)(hsv.val[2]);
if (hsv[0] != 100) {
hsv[0] = 0;
hsv[1] = 0;
hsv[2] = 0;
}
}
}
cout << "$$ Hue(H), Saturation(S), Brightness(V) $$" << filenames[t] << " !! \n\n";
cout << "H: " << h / corow * 360 / 180 << " % \n"; //
cout << "S: " << s / corow * 100 / 255 << " % \n";
cout << "V: " << v / corow * 100 / 255 << " % \n\n";
std::ofstream file("D:\\data\\dfdf\\result_4.txt", std::ios_base::app);
file << v / corow * 100 / 255 << " \n"; // v value
file.close();
}
}
As you can see the imageAnalysis() function, I added std::string folder for the path of extracted images from video clip. However, when I applied this code, I have really weird results like below..
enter image description here
I thought I am supposed to get color value from every 24th image but as you see the results above, I got color values from all images in random order.
Thank you in advance.
It was really nice to learn how to code in efficient way!!
Just to clear the error you mentioned about CV_CAP_PROP_POS_MSEC in your comments:
when I apply CV_CAP_PROP_POS_MSEC to my code, I found some error
messages like "CV_CAP_PROP_POS_MSEC is not defined."
A lot of the constant values are scoped in OpenCV. That means, CV_CAP_PROP_POS_MSEC is not defined, but cv::CV_CAP_PROP_POS_MSEC is. You can also obtain the FPS with cv::CAP_PROP_FPS.
Now to your code, I would actually do something that does not require to save and load the image, but rather pass the images to be processed, like this:
#include "opencv2/opencv.hpp"
#include <iostream>
int main(){
cv::VideoCapture cap("D:\\data\\extra\\video200ul.avi");
if (!cap.isOpened())
{
std::cout << "Could not open video" << std::endl;
return -1;
}
cv::Ptr<cv::BackgroundSubtractor> pMOG2 = cv::createBackgroundSubtractorMOG2(20, 16, true);
cv::Mat fg_mask, frame;
int count = 0;
const int fps = 24; // you may set here the fps or get them from the video
std::string name, folder;
// with cap.read you can check already if the video ended
while (cap.read(frame)) {
// Background subtraction
if (count % fps == 0) {
pMOG2->apply(frame, fg_mask, 0.001);
// Save foreground mask
name = "mask" + std::to_string(count) + ".png";
bool result = cv::imwrite("D:\\data\\extra\\" + name, frame);
imageAnalysis(frame, count);
}
// at the end of the loop so that the first image is used
++count;
}
cv::waitKey(0);
return 0;
}
And the imageAnalysis function is defined as:
// You can pass cv::Mat as value, it is almost like a smart pointer
void imageAnalysis(cv::Mat frame, int count)
{
cv::Mat ori_image = frame.clone();
cv::rectangle(ori_image, Point(215, 98), Point(245, 110), Scalar(0, 255, 255), 1);
// each imshow needs a waitKey to update the window in which it is being shown
cv::imshow("Original Image", ori_image);
cv::waitKey(1);
cv::Scalar sums;
sums = cv::sum(ori_image);
double totalSum = sums[0] + sums[1] + sums[2];
std::ofstream output("D:\\data\\extra\\mask" + std::to_string(count) + ".txt");
if (totalSum <= 0)
{
std::cout << "$$ RGB percentage $$" << std::endl << std::endl;
std::cout << "R: " << 100.0 / 3 << std::endl;
std::cout << "G: " << 100.0 / 3 << std::endl;
std::cout << "B: " << 100.0 / 3 << std::endl << std::endl;
output << "$$ RGB percentage $$" << std::endl << std::endl;
output << "R: " << 100.0 / 3 << std::endl;
output << "G: " << 100.0 / 3 << std::endl;
output << "B: " << 100.0 / 3 << std::endl << std::endl;
}
else {
std::cout << "$$ RGB percentage $$" << std::endl << std::endl;
std::cout << "R: " << sums[2] / totalSum * 100 << std::endl; // red value
std::cout << "G: " << sums[1] / totalSum * 100 << std::endl; // green value
std::cout << "B: " << sums[0] / totalSum * 100 << std::endl << std::endl; // blue value
output << "$$ RGB percentage $$" << std::endl << std::endl;
output << "R: " << sums[2] / totalSum * 100 << std::endl; // red value
output << "G: " << sums[1] / totalSum * 100 << std::endl; // green value
output << "B: " << sums[0] / totalSum * 100 << std::endl << std::endl; // blue value
}
}
Some comments of the code above, I replaced the cap >> frame to cap.read(frame). It is the same functionality, but the later gives a bool result that is false if it could not grab the image, like if the video is over. I change the count add at the end, yo you get the frames 0,23,... this way the first one will be use as well. Finally, you should use the namespaces cv::, std:: etc. This is just best practice, it avoids ambiguities and problems that may arise with certain libraries.
If you do not need the image in disk, but only the analysis, then remove the saving part and pass every frame to the imageAnalysis function, this way you may have more data for your statistics. Also, consider returning the cv:Scalar of sums in the function and then you can do some statistics of the whole second or the whole video.
If you have any question, feel free to ask in the comments.
OpenCV => 3.2
Operating System / Platform => Windows 64 Bit
Compiler => Visual Studio 2015
I am currently working on my project which involves vehicle detection and tracking and estimating and optimizing a cuboid around the vehicle. For that I have accomplished detection and tracking of vehicles and I need to find the 3-D world coordinates of the image points of the edges of the bounding boxes of the vehicles and then estimate the world coordinates of the edges of the cuboid and the project it back to the image to display it.
So, I am new to computer vision and OpenCV, but in my knowledge, I just need 4 points on the image and need to know the world coordinates of those 4 points and use solvePNP in OpenCV to get the rotation and translation vectors (I already have the camera matrix and distortion coefficients). Then, I need to use Rodrigues to transform the rotation vector into a rotation matrix and then concatenate it with the translation vector to get my extrinsic matrix and then multiply the extrinsic matrix with the camera matrix to get my projection matrix. Since my z coordinate is zero, so I need to take off the third column from the projection matrix which gives the homography matrix for converting the 2D image points to 3D world points. Now, I find the inverse of the homography matrix which gives me the homography between the 3D world points to 2D image points. After that I multiply the image points [x, y, 1]t with the inverse homography matrix to get [wX, wY, w]t and the divide the entire vector by the scalar w to get [X, Y, 1] which gives me the X and Y values of the world coordinates.
My code looks like this:
#include "opencv2/opencv.hpp"
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <math.h>
#include <conio.h>
using namespace cv;
using namespace std;
Mat cameraMatrix, distCoeffs, rotationVector, rotationMatrix,
translationVector,extrinsicMatrix, projectionMatrix, homographyMatrix,
inverseHomographyMatrix;
Point point;
vector<Point2d> image_points;
vector<Point3d> world_points;
int main()
{
FileStorage fs1("intrinsics.yml", FileStorage::READ);
fs1["camera_matrix"] >> cameraMatrix;
cout << "Camera Matrix: " << cameraMatrix << endl << endl;
fs1["distortion_coefficients"] >> distCoeffs;
cout << "Distortion Coefficients: " << distCoeffs << endl << endl;
image_points.push_back(Point2d(275, 204));
image_points.push_back(Point2d(331, 204));
image_points.push_back(Point2d(331, 308));
image_points.push_back(Point2d(275, 308));
cout << "Image Points: " << image_points << endl << endl;
world_points.push_back(Point3d(0.0, 0.0, 0.0));
world_points.push_back(Point3d(1.775, 0.0, 0.0));
world_points.push_back(Point3d(1.775, 4.620, 0.0));
world_points.push_back(Point3d(0.0, 4.620, 0.0));
cout << "World Points: " << world_points << endl << endl;
solvePnP(world_points, image_points, cameraMatrix, distCoeffs, rotationVector, translationVector);
cout << "Rotation Vector: " << endl << rotationVector << endl << endl;
cout << "Translation Vector: " << endl << translationVector << endl << endl;
Rodrigues(rotationVector, rotationMatrix);
cout << "Rotation Matrix: " << endl << rotationMatrix << endl << endl;
hconcat(rotationMatrix, translationVector, extrinsicMatrix);
cout << "Extrinsic Matrix: " << endl << extrinsicMatrix << endl << endl;
projectionMatrix = cameraMatrix * extrinsicMatrix;
cout << "Projection Matrix: " << endl << projectionMatrix << endl << endl;
double p11 = projectionMatrix.at<double>(0, 0),
p12 = projectionMatrix.at<double>(0, 1),
p14 = projectionMatrix.at<double>(0, 3),
p21 = projectionMatrix.at<double>(1, 0),
p22 = projectionMatrix.at<double>(1, 1),
p24 = projectionMatrix.at<double>(1, 3),
p31 = projectionMatrix.at<double>(2, 0),
p32 = projectionMatrix.at<double>(2, 1),
p34 = projectionMatrix.at<double>(2, 3);
homographyMatrix = (Mat_<double>(3, 3) << p11, p12, p14, p21, p22, p24, p31, p32, p34);
cout << "Homography Matrix: " << endl << homographyMatrix << endl << endl;
inverseHomographyMatrix = homographyMatrix.inv();
cout << "Inverse Homography Matrix: " << endl << inverseHomographyMatrix << endl << endl;
Mat point2D = (Mat_<double>(3, 1) << image_points[0].x, image_points[0].y, 1);
cout << "First Image Point" << point2D << endl << endl;
Mat point3Dw = inverseHomographyMatrix*point2D;
cout << "Point 3D-W : " << point3Dw << endl << endl;
double w = point3Dw.at<double>(2, 0);
cout << "W: " << w << endl << endl;
Mat matPoint3D;
divide(w, point3Dw, matPoint3D);
cout << "Point 3D: " << matPoint3D << endl << endl;
_getch();
return 0;
I have got the image coordinates of the four known world points and hard-coded it for simplification. image_points contain the image coordinates of the four points and world_points contain the world coordinates of the four points. I am considering the the first world point as the origin (0, 0, 0) in the world axis and using known distance calculating the coordinates of the other four points. Now after calculating the inverse homography matrix, I multiplied it with [image_points[0].x, image_points[0].y, 1]t which is related to the world coordinate (0, 0, 0). Then I divide the result by the third component w to get [X, Y, 1]. But after printing out the values of X and Y, it turns out they are not 0, 0 respectively. What am doing wrong?
The output of my code is like this:
Camera Matrix: [517.0036881709533, 0, 320;
0, 517.0036881709533, 212;
0, 0, 1]
Distortion Coefficients: [0.1128663679798094;
-1.487790079922432;
0;
0;
2.300571896761067]
Image Points: [275, 204;
331, 204;
331, 308;
275, 308]
World Points: [0, 0, 0;
1.775, 0, 0;
1.775, 4.62, 0;
0, 4.62, 0]
Rotation Vector:
[0.661476468596541;
-0.02794460022559267;
0.01206996342819649]
Translation Vector:
[-1.394495345140898;
-0.2454153722672731;
15.47126945512652]
Rotation Matrix:
[0.9995533907649279, -0.02011656447351923, -0.02209848058392758;
0.002297501163799448, 0.7890323093017149, -0.6143474069013439;
0.02979497438726573, 0.6140222623910194, 0.7887261380159]
Extrinsic Matrix:
[0.9995533907649279, -0.02011656447351923, -0.02209848058392758,
-1.394495345140898;
0.002297501163799448, 0.7890323093017149, -0.6143474069013439,
-0.2454153722672731;
0.02979497438726573, 0.6140222623910194, 0.7887261380159,
15.47126945512652]
Projection Matrix:
[526.3071813531748, 186.086785938988, 240.9673682002232, 4229.846989065414;
7.504351145361707, 538.1053336219271, -150.4099339268854, 3153.028471890794;
0.02979497438726573, 0.6140222623910194, 0.7887261380159, 15.47126945512652]
Homography Matrix:
[526.3071813531748, 186.086785938988, 4229.846989065414;
7.504351145361707, 538.1053336219271, 3153.028471890794;
0.02979497438726573, 0.6140222623910194, 15.47126945512652]
Inverse Homography Matrix:
[0.001930136511648154, -8.512427241879318e-05, -0.5103513244724983;
-6.693679705844383e-06, 0.00242178892313387, -0.4917279870709287
-3.451449134581896e-06, -9.595179260534558e-05, 0.08513443835773901]
First Image Point[275;
204;
1]
Point 3D-W : [0.003070864657310213;
0.0004761913292736786;
0.06461112415423849]
W: 0.0646111
Point 3D: [21.04004290792539;
135.683117651025;
1]
Your reasoning is sound, but you are making some mistake in the last division.. or am I missing something?
Your result before W division is:
Point 3D-W :
[0.003070864657310213;
0.0004761913292736786;
0.06461112415423849]
Now we need to normalize this by dividing all the coordinates by W (the 3rd element of the array), as you described in your question. so:
Point 3D-W Normalized =
[0.003070864657310213 / 0.06461112415423849;
0.0004761913292736786 / 0.06461112415423849;
0.06461112415423849 / 0.06461112415423849]
Which results in:
Point 3D-W Normalized =
[0.047528420183179314;
0.007370113668614144;
1.0]
Which is damn close to [0,0].
I am getting incorrect conversions from polar to cartesian coordinates and vice versa. My code produces weird points like (1,-0). Im using this calculator to check my conversions. Also one of the conversions is completely wrong when I convert back to cartesian coordinates.
Point b: (0,1) => (1,1.5708) => (0,0)
#include <math.h>
#include <iostream>
/* Title: Polar - Cartesian Coordinate Conversion
* References: HackerRank > All Domains > Mathematics > Geometry > Polar Angles
* Cartesian to Polar: (radius = sqrt(x^2 + y^2), theta = atan(y/x))
* Polar to Cartesian: (x = radius*cos(theta), y = radius*sin(theta))
*/
//General 2D coordinate pair
struct point{
point(float a_val, float b_val) : a(a_val), b(b_val){;};
point(void){;};
float a, b;
};
//Converts 2D Cartesian coordinates to 2D Polar coordinates
point to_polar(/*const*/ point& p){//*** Conversion of origin result in (r, -nan) ***
point ans(sqrt(pow(p.a,2) + pow(p.b,2)), atan(p.b/p.a));
return ans;
}
//Converts 2D Polar coordinates to 2D Cartesian coordinates
point to_cartesian(/*const*/ point& p){
point ans(p.a * cos(p.b), p.a * sin(p.b));
return ans;
}
//Outputs 2D coordinate pair
std::ostream& operator<<(std::ostream& stream, const point& p){
stream << "(" << p.a << "," << p.b << ")";
return stream;
}
int main(){
//Test Points - Cartesian
point a(0, 0);
point b(0, 1);
point c(1, 0);
point d(0,-1);
point e(-1,0);
//Print Cartesian/Rectangular points
std::cout << "Cartesian Coordinates:" << std::endl;
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
std::cout << d << std::endl;
std::cout << e << std::endl;
//Print Cartesian to Polar
std::cout << "Polar Coordinates:" << std::endl;
std::cout << to_polar(a) << std::endl;//Failure (0,-nan)
std::cout << to_polar(b) << std::endl;//Success
std::cout << to_polar(c) << std::endl;//Success
std::cout << to_polar(d) << std::endl;//Success
std::cout << to_polar(e) << std::endl;//Failure (1,-0)
//Print Polar to Cartesian
std::cout << "Cartesian Coordinates:" << std::endl;
std::cout << to_cartesian(a) << std::endl;//Success
std::cout << to_cartesian(b) << std::endl;//Failure (0,0)
std::cout << to_cartesian(c) << std::endl;//Success
std::cout << to_cartesian(d) << std::endl;//Failure (0,-0)
std::cout << to_cartesian(e) << std::endl;//Failure (-1,-0)
return 0;
}
You are converting to cartesian the points which are in cartesian already. What you want is:
std::cout << "Cartesian Coordinates:" << std::endl;
std::cout << to_cartesian(to_polar(a)) << std::endl;
std::cout << to_cartesian(to_polar(b)) << std::endl;
//...
Edit: using atan2 solves the NaN problem, (0, 0) is converted to (0, 0) which is fine.
As a first step, you need to switch to atan2 instead of atan in your conversion to polar coordinates. atan gives wrong results for half the plane.
hi, i'm trying to play a little bit with Mat class.
I want to do a product element wise between two images, the c++/opencv port of MATLAB immultiply.
This is my code:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
Mat imgA, imgB;
Mat imgAB;
Mat product;
void printMinMax(Mat m, string s) {
double minVal;
double maxVal;
Point minLoc;
Point maxLoc;
minMaxLoc( m, &minVal, &maxVal, &minLoc, &maxLoc );
cout << "min val in " << s << ": " << minVal << endl;
cout << "max val in " << s << ": " << maxVal << endl;
}
int main(int /*argc*/, char** /*argv*/) {
cout << "OpenCV version: " << CV_MAJOR_VERSION << " " << CV_MINOR_VERSION << endl;
imgA = imread("test1.jpg");
cout << "original image size: " << imgA.rows << " " << imgA.cols << endl;
cout << "original type: " << imgA.type() << endl;
cvtColor(imgA, imgA, CV_BGR2GRAY);
printMinMax(imgA, "imgA");
imgB = imread("test2.jpg");
cout << "original image size: " << imgB.rows << " " << imgB.cols << endl;
cout << "original type: " << imgB.type() << endl;
cvtColor(imgB, imgB, CV_BGR2GRAY);
printMinMax(imgB, "imgB");
namedWindow("originals", CV_WINDOW_AUTOSIZE);
namedWindow("product", CV_WINDOW_AUTOSIZE);
imgAB = Mat( max(imgA.rows,imgB.rows), imgA.cols+imgB.cols, imgA.type());
imgA.copyTo(imgAB(Rect(0, 0, imgA.cols, imgA.rows)));
imgB.copyTo(imgAB(Rect(imgA.cols, 0, imgB.cols, imgB.rows)));
product = imgA.mul(imgB);
printMinMax(product, "product");
while( true )
{
char c = (char)waitKey(10);
if( c == 27 )
{ break; }
imshow( "originals", imgAB );
imshow( "product", product );
}
return 0;
}
here is the result:
OpenCV version: 2 4
original image size: 500 500
original type: 16
min val in imgA: 99
max val in imgA: 255
original image size: 500 500
original type: 16
min val in imgB: 0
max val in imgB: 255
init done
opengl support available
min val in product: 0
max val in product: 255
I think that max value in the product has to be greater than 255, but is truncated to 255 because the type of the two matrixes is 16.
I have tried to convert the matrixes to CV_32F but the maxVal in the product is 64009 (a number that i don't understand)
Thanks to Wajih comment i have done some basic test, and some basic debug, and i got i work perfectly. I think this could become a mini tutorial on alpha blending and image multiply, but for now is only a few lines of commented code.
note that the 2 images must be of the same size.. and for sure some error checking should be done for a solid code..
Hope it helps someone! And, of course, if you have some hints to make this code more readable or more compact (one-liner guys are very appreciate!) or efficient.. just comment, thank you a lot!
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
void printMinMax(Mat m, string name) {
double minVal;
double maxVal;
Point minLoc;
Point maxLoc;
if(m.channels() >1) {
cout << "ERROR: matrix "<<name<<" must have 1 channel for calling minMaxLoc" << endl;
}
minMaxLoc( m, &minVal, &maxVal, &minLoc, &maxLoc );
cout << "min val in " << name << ": " << minVal << " in loc: " << minLoc << endl;
cout << "max val in " << name << ": " << maxVal << " in loc: " << maxLoc << endl;
}
int main(int /*argc*/, char** /*argv*/) {
cout << "OpenCV version: " << CV_MAJOR_VERSION << " " << CV_MINOR_VERSION << endl; // 2 4
Mat imgA, imgB;
Mat imgAB;
Mat product;
// fast matrix creation, comma-separated initializer
// example1: create a matrix with value from 0 to 255
imgA = Mat(3, 3, CV_8UC1);
imgA = (Mat_<uchar>(3,3) << 0,1,2,3,4,5,6,7,255);
cout << "test Mat 3x3" << endl << imgA << endl;
// not that if a value exceed 255 it is truncated at value%256
imgA = (Mat_<uchar>(3,3) << 0,1, 258 ,3,4,5,6,7,255);
cout << "test Mat 3x3 with last element truncated to 258%256=2" << endl << imgA << endl;
// create a second matrix
imgB = Mat(3, 3, CV_8UC1);
imgB = (Mat_<uchar>(3,3) << 0,1,2,3,4,5,6,7,8);
// now the matrix product. we are multiplying a value that can goes from 0-255 with another 0-255 value..
// the edge cases are "min * min" and "max * max",
// that means: our product is a function that return a value in the domain 0*0-255*255 ; 0-65025
// ah, ah! this number exceed the Mat U8C1 domain!, we need different data types.
// we need a bigger one.. let's say 32FC1
Mat imgA_32FC1 = imgA.clone();
imgA_32FC1.convertTo(imgA_32FC1, CV_32FC1);
Mat imgB_32FC1 = imgB.clone();
imgB_32FC1.convertTo(imgB_32FC1, CV_32FC1);
// after conversion.. value are scaled?
cout << "imgA after conversion:" << endl << imgA_32FC1 << endl;
cout << "imgB after conversion:" << endl << imgB_32FC1 << endl;
product = imgA_32FC1.mul( imgB_32FC1 );
// note: the product values are in the range 0-65025
cout << "the product:" << endl << product << endl;
// now, this does not have much sense, because we started from a 0-255 range Mat and now we have a 0-65025 that is nothing..
// it is not uchar range and it is not float range (that is a lot bigger than that)
// so, we can normalize back to 0-255
// what do i mean with 'normalize' now?
// i mean: scale all values for a constant that maps 0 to 0 and 65025 to 255..
product.convertTo(product, CV_32FC1, 1.0f/65025.0f * 255);
// but it is still a 32FC1.. not as the start matix..
cout << "the product, normalized back to 0-255, still in 32FC1:" << endl << product << endl;
product.convertTo(product, CV_8UC1);
cout << "the product, normalized back to 0-255, now int 8UC1:" << endl << product << endl;
cout << "-----------------------------------------------------------" << endl;
// real stuffs now.
imgA = imread("test1.jpg");
cvtColor(imgA, imgA, CV_BGR2GRAY);
imgB = imread("test2.jpg");
cvtColor(imgB, imgB, CV_BGR2GRAY);
imgA_32FC1 = imgA.clone();
imgA_32FC1.convertTo(imgA_32FC1, CV_32FC1);
imgB_32FC1 = imgB.clone();
imgB_32FC1.convertTo(imgB_32FC1, CV_32FC1);
product = imgA_32FC1.mul( imgB_32FC1 );
printMinMax(product, "product");
product.convertTo(product, CV_32FC1, 1.0f/65025.0f * 255);
product.convertTo(product, CV_8UC1);
// concat two images in one big image
imgAB = Mat( max(imgA.rows,imgB.rows), imgA.cols+imgB.cols, imgA.type());
imgA.copyTo(imgAB(Rect(0, 0, imgA.cols, imgA.rows)));
imgB.copyTo(imgAB(Rect(imgA.cols, 0, imgB.cols, imgB.rows)));
namedWindow("originals", CV_WINDOW_AUTOSIZE);
namedWindow("product", CV_WINDOW_AUTOSIZE);
while( true )
{
char c = (char)waitKey(10);
if( c == 27 )
{ break; }
imshow( "originals", imgAB );
imshow( "product", product );
}
return 0;
}
You are right, you should convert your matrices imgA, imgB to say CV32FC1 type. Since the max values in this matrices is 255, the maximum possible value is 65025. However, the maximum at imgA and imgB may not be in the same location, so 64009 is quite possible.