Crop an image with given angle - c++

I have the center, the angle of inclination (direction) and the measures of the sides of a sub-image that I want to crop from another image, for example:
to:
I managed to put it with the right inclination using:
Mat img;
Point center;
float angle;
Mat rotation = getRotationMatrix2D(center, angle, 1.0);
warpAffine(img, img, rotation, img.size());
But I dont know how to cut that area with the given sides... How can I do it?

If you can rotate the image, after that you just need one of the corners position (for instance, bottom-right corner) and the width and height of the image you want to crop.
With that said, you can set a ROI (region of interest) and crop with
cv::Rect RegionOfInterest(top_left_x, top_left_y, rectangle_width, rectangle_height);
cv::Mat outputImage;
outputImage = originalImage(RegionOfInterest).clone();
where top_left_x and top_left_y are your top left corner coordinates, and rectangle_width and rectangle_height are the width and height of the rectangle you are interested in extract.

Related

How to calculate the black / white ratio of pixels inside a contour

How is it possible to calculate the black / white ratio of the pixels inside the outline of a contour (not the bounding box)?
The image is pre-processed with cv::threshold(src, img, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); and then inverted img = 255 - img;
I look for the retangular outline of the table (contour) via cv::RETR_EXTERNAL.. I want to calculate the black pixels inside the contour
There can be other components in the image so I can't just count all non-zero pixels
This is the original image before binarized and inverted
I think there's some confusion about terminology. A contour is simply a sequence of points. If you draw them as a closed polygon (e.g. with cv::drawContours), all the points inside the polygon will be white.
You can however use this mask to count the white or black pixels on your thresholded image:
cv::Mat1b bw_image = ...
std::vector<std::vector<cv::Point>> contours;
cv::findContours(bw_image, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
for(size_t i=0; i<contours.size(); ++i)
{
cv::Mat1b contour_mask(bw_image.rows, bw_image.cols, uchar(0));
cv::drawContours(contour_mask, contours, i, Scalar(255), cv::FILLED);
int total_white_inside_contour = cv::countNonZero(mask);
int white_on_image_inside_contour = cv::countNonZero(bw_image & mask);
int black_on_image_inside_contour = total_white_inside_contour - white_on_image_inside_contour;
}
You cannot calculate the white and black ratio of a contour, because what is a contour? A group of white pixels which are connected which each other calls contour, so a contour does not contain any black pixel if it does, it calls hole inside the contour.
And also a contour does not have a specific shape.
So you can do it by Bounding Rectangle the rectangle around the contour then you will be to calculate the black and white ratio inside the rectangle.

Remove black Background from an image.

I'm new to image processing and development. I have used opencv, There I need to extract circle from a given image. That circle given x, y coordinates are (radius) in Oder to do that I used following code. But my problem is I have to take black rectangle. So the image patch having unwanted black pixels. How do I save just only circle?
my code
double save_key_points(Mat3b img, double x, double y, double radius, string
filename, string foldername)
{
// print image height and width first and check.
Vec3f circ(x, y, radius);
// Draw the mask: white circle on black background
Mat1b mask(img.size(), uchar(0));
circle(mask, Point(circ[0], circ[1]), circ[2], Scalar(255), CV_FILLED);
// Compute the bounding box
Rect bbox(circ[0] - circ[2], circ[1] - circ[2], 2 * circ[2], 2 * circ[2]);
// Create a black image
Mat3b res(img.size(), Vec3b(0, 0, 0));
// Copy only the image under the white circle to black image
img.copyTo(res, mask);
// Crop according to the roi
res = res(bbox);
//remove black but doesn't work.
Mat tmp, alpha;
threshold(res, alpha, 100, 255, THRESH_BINARY);
// Save the image
string path = "C:\\Users\\bb\\Desktop\\test_results\\test_case8\\" + foldername + filename + ".png";
imwrite(path, res);
Mat keypointimg = imread(path, CV_LOAD_IMAGE_GRAYSCALE);
//print the cordinate of one patch.
cordinate_print(keypointimg, radius);
}
(Here i want without black background)
If I understand what you are asking correctly you could remove the black from an image you can use a mask. The mask can highlight anything that is of a certain colour or in your case the shade of black. Check out the link for this implementation and see if it is what you are looknig for. It is in python but can be easily adapted.
Image Filtering

How to maintain white background when using opencv warpaffine

I'm trying to rotate image using
void rotate(cv::Mat& src, double angle, cv::Mat& dst)
{
int len = std::max(src.cols, src.rows);
cv::Point2f pt(len / 2., len / 2.);
cv::Mat r = cv::getRotationMatrix2D(pt, angle, 1.0);
cv::warpAffine(src, dst, r, cv::Size(src.cols, src.rows));
}
by giving angle, source and destination image. Rotation works correctly as follows.
I want to make black areas white. I have tried with
cv::Mat dst = cv::Mat::ones(src.cols, src.rows, src.type());
before calling rotate, but no change in result. How can I achieve this?
Note: I am looking for solution which achieve this while doing the rotation. obviously by making black areas white after the rotation this can be achieved.
You will want to use the borderMode and borderValue arguments of the warpAffine function to accomlish this. By setting the mode to BORDER_CONSTANT it will use a constant value for border pixels (i.e. outside the image), and you can set the value to the constant value you want to use (i.e. white). It would look something like:
cv::warpAffine(src, dst, r,
cv::Size(src.cols, src.rows),
cv::INTER_LINEAR,
cv::BORDER_CONSTANT,
cv::Scalar(255, 255, 255));
For more details see the OpenCV API Documentation.

3D Geometry projecting a point in space to an image given FOV and angles

I'm attempting to project a 3D point in Cartesian space onto an image, so it is shown as a circle at the location on the image it would be seen if a certain camera was looking at it.
Knows:
The camera has horizontal field of view, vertical FOV, azimuth and elevation.
It is located at (0,0,0) although being able to change this would be good.
The image has a certain size.
The point in space has a know X, Y, Z location.
I'm using the very good C++ library MathGeoLib
In my attempt, changing azimuth or elevation does not work, but the projection seems to work ok if rotation is ignored.
How do you convert from a real world point into pixel location of an image providing that you know where the where the camera is, which direction it is looking and it's field of view?
Frustum MyClass::GetCameraFrustrum()
{
Frustum frustrum;
frustrum.SetPerspective(DegToRad(_horizontal_field_of_view), DegToRad(_vertical_filed_of_view));
// Looking straight down the z axis
frustrum.SetFront(vec(0, 0, 1));
frustrum.SetUp(vec(0, 1, 0));
frustrum.SetPos(vec(0,0,0));
frustrum.SetKind(FrustumSpaceGL, FrustumRightHanded);
frustrum.SetViewPlaneDistances(1, 2000);
float3x3 rotate_around_x = float3x3::RotateX(DegToRad(_angle_azimuth));
frustrum.Transform(rotate_around_x);
float3x3 rotate_around_y = float3x3::RotateY(DegToRad(_angle_elevation));
frustrum.Transform(rotate_around_y);
return frustrum;
}
void MyClass::Test()
{
cv::Mat image = cv::Mat::zeros(2000, 2000, CV_8UC1);
Frustum camera_frustrum = GetCameraFrustrum();
vec projection = camera_frustrum.Project(_position_of_object);
cv::Point2d location_on_image(projection.x * image.cols, projection.y * image.rows);
LOG_DEBUG << location_on_image;
cv::circle(image, location_on_image, 5, cv::Scalar(255, 0, 0), 3, cv::LINE_AA);
}

How to create a semi transparent shape?

I would like to know how to draw semi-transparent shapes in OpenCV, similar to those in the image below (from http://tellthattomycamera.wordpress.com/)
I don't need those fancy circles, but I would like to be able to draw a rectangle, e.g, on a 3 channel color image and specify the transparency of the rectangle, something like
rectangle (img, Point (100,100), Point (300,300), Scalar (0,125,125,0.4), CV_FILLED);
where 0,125,125 is the color of the rectangle and 0.4 specifies the transparency.
However OpenCV doesn't have this functionality built into its drawing functions. How can I draw shapes in OpenCV so that the original image being drawn on is partially visible through the shape?
The image below illustrates transparency using OpenCV. You need to do an alpha blend between the image and the rectangle. Below is the code for one way to do this.
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
int main( int argc, char** argv )
{
cv::Mat image = cv::imread("IMG_2083s.png");
cv::Mat roi = image(cv::Rect(100, 100, 300, 300));
cv::Mat color(roi.size(), CV_8UC3, cv::Scalar(0, 125, 125));
double alpha = 0.3;
cv::addWeighted(color, alpha, roi, 1.0 - alpha , 0.0, roi);
cv::imshow("image",image);
cv::waitKey(0);
}
In OpenCV 3 this code worked for me:
cv::Mat source = cv::imread("IMG_2083s.png");
cv::Mat overlay;
double alpha = 0.3;
// copy the source image to an overlay
source.copyTo(overlay);
// draw a filled, yellow rectangle on the overlay copy
cv::rectangle(overlay, cv::Rect(100, 100, 300, 300), cv::Scalar(0, 125, 125), -1);
// blend the overlay with the source image
cv::addWeighted(overlay, alpha, source, 1 - alpha, 0, source);
Source/Inspired by: http://bistr-o-mathik.org/2012/06/13/simple-transparency-in-opencv/
Adding to Alexander Taubenkorb's answer, you can draw random (semi-transparent) shapes by replacing the cv::rectangle line with the shape you want to draw.
For example, if you want to draw a series of semi-transparent circles, you can do it as follows:
cv::Mat source = cv::imread("IMG_2083s.png"); // loading the source image
cv::Mat overlay; // declaring overlay matrix, we'll copy source image to this matrix
double alpha = 0.3; // defining opacity value, 0 means fully transparent, 1 means fully opaque
source.copyTo(overlay); // copying the source image to overlay matrix, we'll be drawing shapes on overlay matrix and we'll blend it with original image
// change this section to draw the shapes you want to draw
vector<Point>::const_iterator points_it; // declaring points iterator
for( points_it = circles.begin(); points_it != circles.end(); ++points_it ) // circles is a vector of points, containing center of each circle
circle(overlay, *points_it, 1, (0, 255, 255), -1); // drawing circles on overlay image
cv::addWeighted(overlay, alpha, source, 1 - alpha, 0, source); // blending the overlay (with alpha opacity) with the source image (with 1-alpha opacity)
For C++, I personally like the readability of overloaded operators for scalar multiplication and matrix addition:
... same initial lines as other answers above ...
// blend the overlay with the source image
source = source * (1.0 - alpha) + overlay * alpha;