I want to superimpose an image on a previously detected item using haarcascade classifiers by using warpPerspective. The image seems properly scaled but the rest of the image becomes black.
I have tried to modify transparency of source and destination image but I do not know if it was successful.
image without warpPerspective
image with warpPerspective
Rect r = boundingRects.carRects.at(i).rect; // points of the object in the image.
vector<Point2d> imagePoints;
imagePoints.push_back(Point2d(r.x, r.y));
imagePoints.push_back(Point2d(r.x + r.width, r.y));
imagePoints.push_back(Point2d(r.x + r.width, r.y+ r.height));
imagePoints.push_back(Point2d(r.x, r.y + r.height));
dimensions.push_back(Point2d(0, 0)); // dimensions of the source image
dimensions.push_back(Point2d(899, 0));
dimensions.push_back(Point2d(899, 539));
dimensions.push_back(Point2d(0, 539));
H = findHomography(carAlertObject.dimensions, imagePoints); // get homography matrix
warpPerspective(carAlertObject.img, img, H,img.size()); // carAlertObject contains the source image and dimensions, img is the destination image
The problem was that I had to use a mask image to apply warpPerspective function and then do the sum of that image and destination image to have a correct result.
Related
I'm new to image processing and development. I have pixel coordinates of an image. By connecting each coordinate can be obtained a triangle. I want to extract pixels inside pixels of giving coordinates (pixels within a triangle)
Cordinates as follows.
1(x,y) -> (146 , 548)
2(x,y) -> (155, 548)
3(x,y) -> (149.6 , 558.1)
How do i take pixels that are inbound of above coordinates. Any help is appreciated. Thank you.
You should apply mask on your image.
Example code:
First you should load your image:
//load default image
Mat image = cv::imread("/home/fabio/code/lena.jpg", cv::IMREAD_GRAYSCALE);
Then create a mask for your image and apply the triangle points to the mask.
//mask image definition
cv::Mat mask = cv::Mat::zeros(image.size(), image.type());
//triangle definition (example points)
vector<Point> points;
points.push_back( Point(100,70));
points.push_back( Point(60,150));
points.push_back( Point(190,120));
//apply triangle to mask
fillConvexPoly( mask, points, Scalar( 255 ));
After that your mask will look like this:
Finally create the final image applying the mask to the original image:
//final image definition
cv::Mat finalImage = cv::Mat::zeros(image.size(), image.type());
image.copyTo(finalImage, mask);
Result:
Is that what you're looking for?
There is a thread talking about this, the solution given there is easily applicable to your case, just replacing the shape used in the example provided.
Here is the link : opencv-binary-image-mask-for-image-analysis-in-c
I am trying to rotate an image in x, y and z axis as in this.
The image should not be cropped while rotating So I am doing this
Mat src = imread("path");
int diagonal = (int)sqrt(src.cols*src.cols+src.rows*src.rows);
int newWidth = diagonal;
int newHeight =diagonal;
Mat targetMat(newWidth, newHeight, src.type());
I am creating a bigger image targetMat. The input image is a png image.
But I want this image as a transparent image. So I tried this
Mat targetMat(newWidth, newHeight, src.type(), cv::Scalar(0,0,0,0));
But the output image was
What I need is (Transparent image is here)
So what change do I have to do?
The problem is, that your input image is type CV_8UC3 but you need CV_8UC4 to use the alpha channel. So try Mat targetMat(newHeight, newWidth, CV_8UC4, cv::Scalar(0,0,0,0)); or cvtColor of src before creation of new mat
To use your original image, there are two possibilities:
use cv::cvtColor(src, src, CV_BGR2BGRA) (and adjust later code to use a 4 channel matrix - cv::Vec4b instead of cv::Vec3b etc)
if your input file is a .png with alpha channel you can use the CV_LOAD_IMAGE_ANYDEPTH (hope this is the right one) flag to load it as a CV_xxC4 image (might be 16 bit too) and to use the original alpha values.
I create a Bird-View-Image with the warpPerspective()-function like this:
warpPerspective(frame, result, H, result.size(), CV_WARP_INVERSE_MAP, BORDER_TRANSPARENT);
The result looks very good and also the border is transparent:
Bird-View-Image
Now I want to put this image on top of another image "out". I try doing this with the function warpAffine like this:
warpAffine(result, out, M, out.size(), CV_INTER_LINEAR, BORDER_TRANSPARENT);
I also converted "out" to a four channel image with alpha channel according to a question which was already asked on stackoverflow:
Convert Image
This is the code: cvtColor(out, out, CV_BGR2BGRA);
I expected to see the chessboard but not the gray background. But in fact, my result looks like this:
Result Image
What am I doing wrong? Do I forget something to do? Is there another way to solve my problem? Any help is appreciated :)
Thanks!
Best regards
DamBedEi
I hope there is a better way, but here it is something you could do:
Do warpaffine normally (without the transparency thing)
Find the contour that encloses the image warped
Use this contour for creating a mask (white values inside the image warped, blacks in the borders)
Use this mask for copy the image warped into the other image
Sample code:
// load images
cv::Mat image2 = cv::imread("lena.png");
cv::Mat image = cv::imread("IKnowOpencv.jpg");
cv::resize(image, image, image2.size());
// perform warp perspective
std::vector<cv::Point2f> prev;
prev.push_back(cv::Point2f(-30,-60));
prev.push_back(cv::Point2f(image.cols+50,-50));
prev.push_back(cv::Point2f(image.cols+100,image.rows+50));
prev.push_back(cv::Point2f(-50,image.rows+50 ));
std::vector<cv::Point2f> post;
post.push_back(cv::Point2f(0,0));
post.push_back(cv::Point2f(image.cols-1,0));
post.push_back(cv::Point2f(image.cols-1,image.rows-1));
post.push_back(cv::Point2f(0,image.rows-1));
cv::Mat homography = cv::findHomography(prev, post);
cv::Mat imageWarped;
cv::warpPerspective(image, imageWarped, homography, image.size());
// find external contour and create mask
std::vector<std::vector<cv::Point> > contours;
cv::Mat imageWarpedCloned = imageWarped.clone(); // clone the image because findContours will modify it
cv::cvtColor(imageWarpedCloned, imageWarpedCloned, CV_BGR2GRAY); //only if the image is BGR
cv::findContours (imageWarpedCloned, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
// create mask
cv::Mat mask = cv::Mat::zeros(image.size(), CV_8U);
cv::drawContours(mask, contours, 0, cv::Scalar(255), -1);
// copy warped image into image2 using the mask
cv::erode(mask, mask, cv::Mat()); // for avoid artefacts
imageWarped.copyTo(image2, mask); // copy the image using the mask
//show images
cv::imshow("imageWarpedCloned", imageWarpedCloned);
cv::imshow("warped", imageWarped);
cv::imshow("image2", image2);
cv::waitKey();
One of the easiest ways to approach this (not necessarily the most efficient) is to warp the image twice, but set the OpenCV constant boundary value to different values each time (i.e. zero the first time and 255 the second time). These constant values should be chosen towards the minimum and maximum values in the image.
Then it is easy to find a binary mask where the two warp values are close to equal.
More importantly, you can also create a transparency effect through simple algebra like the following:
new_image = np.float32((warp_const_255 - warp_const_0) *
preferred_bkg_img) / 255.0 + np.float32(warp_const_0)
The main reason I prefer this method is that openCV seems to interpolate smoothly down (or up) to the constant value at the image edges. A fully binary mask will pick up these dark or light fringe areas as artifacts. The above method acts more like true transparency and blends properly with the preferred background.
Here's a small test program that warps with transparent "border", then copies the warped image to a solid background.
int main()
{
cv::Mat input = cv::imread("../inputData/Lenna.png");
cv::Mat transparentInput, transparentWarped;
cv::cvtColor(input, transparentInput, CV_BGR2BGRA);
//transparentInput = input.clone();
// create sample transformation mat
cv::Mat M = cv::Mat::eye(2,3, CV_64FC1);
// as a sample, just scale down and translate a little:
M.at<double>(0,0) = 0.3;
M.at<double>(0,2) = 100;
M.at<double>(1,1) = 0.3;
M.at<double>(1,2) = 100;
// warp to same size with transparent border:
cv::warpAffine(transparentInput, transparentWarped, M, transparentInput.size(), CV_INTER_LINEAR, cv::BORDER_TRANSPARENT);
// NOW: merge image with background, here I use the original image as background:
cv::Mat background = input;
// create output buffer with same size as input
cv::Mat outputImage = input.clone();
for(int j=0; j<transparentWarped.rows; ++j)
for(int i=0; i<transparentWarped.cols; ++i)
{
cv::Scalar pixWarped = transparentWarped.at<cv::Vec4b>(j,i);
cv::Scalar pixBackground = background.at<cv::Vec3b>(j,i);
float transparency = pixWarped[3] / 255.0f; // pixel value: 0 (0.0f) = fully transparent, 255 (1.0f) = fully solid
outputImage.at<cv::Vec3b>(j,i)[0] = transparency * pixWarped[0] + (1.0f-transparency)*pixBackground[0];
outputImage.at<cv::Vec3b>(j,i)[1] = transparency * pixWarped[1] + (1.0f-transparency)*pixBackground[1];
outputImage.at<cv::Vec3b>(j,i)[2] = transparency * pixWarped[2] + (1.0f-transparency)*pixBackground[2];
}
cv::imshow("warped", outputImage);
cv::imshow("input", input);
cv::imwrite("../outputData/TransparentWarped.png", outputImage);
cv::waitKey(0);
return 0;
}
I use this as input:
and get this output:
which looks like ALPHA channel isn't set to ZERO by warpAffine but to something like 205...
But in general this is the way I would do it (unoptimized)
I am new to OpenCV2 and working on a project in emotion recognition and would like to align a facial image in relation to a reference facial image. I would like to get the image translation working before moving to rotation. Current idea is to run a search within a limited range on both x and y coordinates and use the sum of squared differences as error metric to select the optimal x/y parameters to align the image. I'm using the OpenCV face_cascade function to detect the face images, all images are resized to a fixed (128x128). Question: Which parameters of the Mat image do I need to modify to shift the image in a positive/negative direction on both x and y axis? I believe setImageROI is no longer supported by Mat datatypes? I have the ROIs for both faces available however I am unsure how to use them.
void alignImage(vector<Rect> faceROIstore, vector<Mat> faceIMGstore)
{
Mat refimg = faceIMGstore[1]; //reference image
Mat dispimg = faceIMGstore[52]; // "displaced" version of reference image
//Rect refROI = faceROIstore[1]; //Bounding box for face in reference image
//Rect dispROI = faceROIstore[52]; //Bounding box for face in displaced image
Mat aligned;
matchTemplate(dispimg, refimg, aligned, CV_TM_SQDIFF_NORMED);
imshow("Aligned image", aligned);
}
The idea for this approach is based on Image Alignment Tutorial by Richard Szeliski Working on Windows with OpenCV 2.4. Any suggestions are much appreciated.
cv::Mat does support ROI. (But it does not support COI - channel-of-interest.)
To apply ROI you can use operator() or special constructor:
Mat refimgROI = faceIMGstore[1](faceROIstore[1]); //reference image ROI
Mat dispimgROI(faceIMGstore[52], faceROIstore[52]); // "displaced" version of reference image ROI
And to find the best position inside a displaced image you can utilize matchTemplate function.
Based on your comments I can suggest the following code which will find the best position of reference patch nearby the second (displaced) patch:
Mat ref = faceIMGstore[1](faceROIstore[1]);
Mat disp = faceIMGstore[52](faceROIstore[52]);
disp = disp.adjustROI(5,5,5,5); //allow 5 pixel max adjustment in any direction
if(disp.cols < ref.cols || disp.rows < ref.rows)
return 0;
Mat map;
cv::matchTemplate( disp, ref, map, CV_TM_SQDIFF_NORMED );
Point minLoc;
cv::minMaxLoc( map, 0, &minLoc );
Mat adjusted = disp(Rect(minLoc.x, minLoc.y, ref.cols, ref.rows));
The problem is solved....I used cvGet2D,below is the sample code
CvScalar s;
s=cvGet2D(src_Image,pixel[i].x,pixel[i].y);
cvSet2D(dst_Image,pixel[i].x,pixel[i].y,s);
Where src_Iamge and dst_Image is the source and destination image correspondingly and pixel[i] is the selected pixel i wanted to draw in the dst image. I have include the real out image below.
have an source Ipl image, I want to copy some of the part of the image to a new destination image pixel by pixel. can any body tell me how can do it? I use c,c++ in opencv. For example if the below image is source image,
The real output image
EDIT:
I can see the comments suggesting cvGet2d. I think, if you just want to show "points", it is best to show them with a small neighbourhood so they can be seen where they are. For that you can draw white filled circles with origins at (x,y), on a mask, then you do the copyTo.
using namespace cv;
Mat m(input_iplimage);
Mat mask=Mat::zeros(m.size(), CV_8UC1);
p1 = Point(x,y);
r = 3;
circle(mask,p1,r, 1); // draws the circle around your point.
floodFill(mask, p1, 1); // fills the circle.
//p2, p3, ...
Mat output = Mat::zeros(m.size(),m.type()); // output starts with a black background.
m.copyTo(output, mask); // copies the selected parts of m to output
OLD post:
Create a mask and copy those pixels:
#include<opencv2/opencv.hpp>
using namespace cv;
Mat m(input_iplimage);
Mat mask=Mat::zeros(m.size(), CV_8UC1); // set mask 1 for every pixel you wanna copy.
Rect roi=Rect(x,y,width,height); // create a rectangle
mask(roi) = 1; // set it to 0.
roi = Rect(x2,y2,w2,h2);
mask(roi)=1; // set the second rectangular area for copying...
Mat output = 100*Mat::ones(m.size(),m.type()); // output with a gray background.
m.copyTo(output, mask); // copy selected areas of m to output
Alternatively you can copy Rect-by-Rect:
Mat m(input_iplimage);
Mat output = 100*Mat::ones(m.size(),m.type()); // output with a gray background.
Rect roi=Rect(x,y,width,height);
Mat m_temp, out_temp;
m_temp=m(roi);
out_temp = output(roi);
m_temp.copyTo(out_temp);
roi=Rect(x2,y2,w2,h2);
Mat m_temp, out_temp;
m_temp=m(roi);
out_temp = output(roi);
m_temp.copyTo(out_temp);
The answer to your question only requires to have look at the OpenCV documentation or just to search in your favourite search engine.
Here you've an answer for Ipl images and for newer Mat data.
For having an output as I see in your images, I'd do it setting ROI's, it's more efficient.