How to extract face from an image? - c++

I have successfully detected a face out of an image having other things in background using OpenCv.
Now I need to extract just the detected part (i.e. face) and convert it into some image format like jpeg or gif to make a face database to use for my neural net training.
How can I do this?

Once you detect the faces, you get opposite corners of a rectangle, which is used to draw rectangles around the face.
Now you can set image ROI ( Region of Interest) , crop the ROI and save it as another image.
/* After detecting the rectangle points, Do as follows */
/* sets the Region of Interest
Note that the rectangle area has to be __INSIDE__ the image */
cvSetImageROI(img1, cvRect(10, 15, 150, 250));
/* create destination image
Note that cvGetSize will return the width and the height of ROI */
IplImage *img2 = cvCreateImage(cvGetSize(img1),
img1->depth,
img1->nChannels);
/* copy subimage */
cvCopy(img1, img2, NULL);
/* always reset the Region of Interest */
cvResetImageROI(img1);
Above code is taken from http://nashruddin.com/OpenCV_Region_of_Interest_(ROI)
Further cvSaveImage function can be used to save image to a file.

try this:
for(i=0;i<(pFaceRectSeq?pFaceRectSeq->total:0);i++)
{
CvRect* r=(CvRect*)cvGetSeqElem(pFaceRectSeq,i);
int width=r->width;
int height=r->height;
cvSetImageROI(pInpImg,*r);
IplImage* pCropImg=cvCreateImage(cvSize(width,height),IPL_DEPTH_8U,3);
cvCopy(pInpImg,pCropImg,NULL);
cvShowImage("Cropped Window",pCropImg);
cvWaitKey(0);
cvResetImageROI(pInpImg);
cvReleaseImage(&pCropImg);
}

Related

OpenCV: PNG image with alpha channel

I'm new to OpenCV and I've done a small POC for reading an image from some URL.
I'm reading the image from an URL using video capture. The code is as follows:
VideoCapture vc;
vc.open("http://files.kurento.org/img/mario-wings.png");
if(vc.isOpened() && vc.grab())
{
cv::Mat logo;
vc.retrieve(logo);
cv::namedWindow("t");
imwrite( "mario-wings-opened.png", logo);
cv::imshow("t", logo);
cv::waitKey(0);
vc.release();
}
This image is not opened correctly, possibly due to alpha channel.
What is the way to preserve alpha channel and get the image correctly?
Any help is appreciated.
-Thanks
Expected output
Actual output
if you are only loading an image, I recommend you to use imread instead, also, you will need to specified the second parameter of imread to load the alpha channel too, that is CV_LOAD_IMAGE_UNCHANGED or cv::IMREAD_UNCHANGED, depending on the version (in the worst case a -1 also works).
As far as I know, the VideoCaptureclass do not load images/video with a 4th channel. Since you are using a web url, loading the image won't work with imread, but you may use any method to download the data (curl for example) and then use imdecode with the data buffer to get the cv::Mat. OpenCV is a library for image processing, not for downloading images.
If you wanna draw it over another image you can do that:
/**
* #brief Draws a transparent image over a frame Mat.
*
* #param frame the frame where the transparent image will be drawn
* #param transp the Mat image with transparency, read from a PNG image, with the IMREAD_UNCHANGED flag
* #param xPos x position of the frame image where the image will start.
* #param yPos y position of the frame image where the image will start.
*/
void drawTransparency(Mat frame, Mat transp, int xPos, int yPos) {
Mat mask;
vector<Mat> layers;
split(transp, layers); // seperate channels
Mat rgb[3] = { layers[0],layers[1],layers[2] };
mask = layers[3]; // png's alpha channel used as mask
merge(rgb, 3, transp); // put together the RGB channels, now transp insn't transparent
transp.copyTo(frame.rowRange(yPos, yPos + transp.rows).colRange(xPos, xPos + transp.cols), mask);
}

substract region from image and keep the borders

I have a 200x200 pixels image and I want to keep only the data for a certain region inside it.
Check the following image:
The whole out square is 200x200 pixels.I want to remove from it the smaller square(white).So ,keep only the information that is included in the blue area.But , I want to keep the 200x200 dimensions.
I tried:
Mat whiteArea;
whiteArea = ImageInitial( Range(50,200) , Range(50,200) );
Size size(200,200);
Mat dst;
resize(whiteArea,dst,size);
Mat FinalImage;
subtract(ImageInitial,dst,FinalImage);
I am resizing the white area because I want to substract it from the initial image.
My problem is that it gives me the initial image.
Maybe the resize is the problem .but then how to substract 2 different sized images?
try to use subimages or use a mask:
// use a roi (nice if your target area is rectangular and you know the position)
Rect whiteArea = Rect(50,50, 200,200); // creates a roi of the inner rect
Mat FinalImage = ImageInitial.clone();
// now set the roi area to zero:
FinalImage (whiteArea).setTo(Scalar(0,0,0));
// or FinalImage(whiteArea) = FinalImage(whiteArea) - FinalImage(whiteArea);
imshow("version 1 with subimage", FinalImage);
waitkey(0);
// or use a mask (nice if that region can has arbitrary shape etc and you have to extract it first):
Scalar lowerColorBound = Scalar(x,y,z); //some BGR values to find the color you want to eliminate
Scalar upperColorBound = Scalar(a,b,c); //some BGR values to find the color you want to eliminate
Mat mask;
inRange(ImageInitial, lowerColorBound, upperColorBound mask)
// use the mask for subtraction:
subtract(ImageInitial, ImageInitial, FinalImage , mask);
imshow("version 2 with mask", FinalImage);
waitkey(0);

Cropping out an image from an existing image

I would like to crop out an image from an existing image. I've taken an image and applied monochrome on it with threshold 98% using imagemagick (is this doable in openCV?)
The resulting image is this:
Now from this Image I would like to crop out another image so that the final image looks like this:
Question
How can I do this in OpenCV? Note, the only reason I want to crop the image is so that I can use this answer to get the part of the text. If there is no need to crop out a new image and instead just concentrate on black part of the image to begin with, that would be great.
If the text at the top and at the bottom are the regions that you want to crop out, if they are always at the same location the solution is easy: just set a ROI that ignores those areas:
#include <cv.h>
#include <highgui.h>
int main(int argc, char* argv[])
{
cv::Mat img = cv::imread(argv[1]);
if (img.empty())
{
std::cout << "!!! imread() failed to open target image" << std::endl;
return -1;
}
/* Set Region of Interest */
int offset_x = 129;
int offset_y = 129;
cv::Rect roi;
roi.x = offset_x;
roi.y = offset_y;
roi.width = img.size().width - (offset_x*2);
roi.height = img.size().height - (offset_y*2);
/* Crop the original image to the defined ROI */
cv::Mat crop = img(roi);
cv::imshow("crop", crop);
cv::waitKey(0);
cv::imwrite("noises_cropped.png", crop);
return 0;
}
Output image:
If the position of the black rectangle, which is your area of interest, is not present on a fixed location then you might want to check out another approach: use the rectangle detection technique:
On the output above, the area you are interested will be 2nd largest rectangle in the image.
On a side note, if you plan to isolate the text later, a simple cv::erode() could remove all the noises in that image so you are left with the white box & text. Another technique to remove noises is to use cv::medianBlur().You can also explore cv::morphologyEx() to do that trick:
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(7, 7), cv::Point(3, 3));
cv::morphologyEx(src, src, cv::MORPH_ELLIPSE, kernel);
A proper solution might even be a combination of these 3. I've demonstrated a little bit of that on Extract hand bones from X-ray image.
A simple solution: scan lines from top down, bottom up, left-right and right-left. Terminate when the number of dark pixels in the line exceeds 50% of the total amount of pixels in the line. This will give you the xmin, xmax, ymin, ymax coordinates to bound your cropping rectangle.

copying ipl image pixel by pixel

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.

With OpenCV, try to extract a region of a picture described by ArrayOfArrays

I am developing some image processing tools in iOS. Currently, I have a contour of features computed, which is of type InputArrayOfArrays.
Declared as:
std::vector<std::vector<cv::Point> > contours_final( temp_contours.size() );
Now, I would like to extract areas of the original RGB picture circled by contours and may further store sub-image as cv::Mat format. How can I do that?
Thanks in advance!
I'm guessing what you want to do is just extract the regions in the the detected contours. Here is a possible solution:
using namespace cv;
int main(void)
{
vector<Mat> subregions;
// contours_final is as given above in your code
for (int i = 0; i < contours_final.size(); i++)
{
// Get bounding box for contour
Rect roi = boundingRect(contours_final[i]); // This is a OpenCV function
// Create a mask for each contour to mask out that region from image.
Mat mask = Mat::zeros(image.size(), CV_8UC1);
drawContours(mask, contours_final, i, Scalar(255), CV_FILLED); // This is a OpenCV function
// At this point, mask has value of 255 for pixels within the contour and value of 0 for those not in contour.
// Extract region using mask for region
Mat contourRegion;
Mat imageROI;
image.copyTo(imageROI, mask); // 'image' is the image you used to compute the contours.
contourRegion = imageROI(roi);
// Mat maskROI = mask(roi); // Save this if you want a mask for pixels within the contour in contourRegion.
// Store contourRegion. contourRegion is a rectangular image the size of the bounding rect for the contour
// BUT only pixels within the contour is visible. All other pixels are set to (0,0,0).
subregions.push_back(contourRegion);
}
return 0;
}
You might also want to consider saving the individual masks to optionally use as a alpha channel in case you want to save the subregions in a format that supports transparency (e.g. png).
NOTE: I'm NOT extracting ALL the pixels in the bounding box for each contour, just those within the contour. Pixels that are not within the contour but in the bounding box are set to 0. The reason is that your Mat object is an array and that makes it rectangular.
Lastly, I don't see any reason for you to just save the pixels in the contour in a specially created data structure because you would then need to store the position for each pixel in order to recreate the image. If your concern is saving space, that would not save you much space if at all. Saving the tightest bounding box would suffice. If instead you wish to just analyze the pixels in the contour region, then save a copy of the mask for each contour so that you can use it to check which pixels are within the contour.
You are looking for the cv::approxPolyDP() function to connect the points.
I shared a similar use of the overall procedure in this post. Check the for loop after the findContours() call.
I think what you're looking for is cv::boundingRect().
Something like this:
using namespace cv;
Mat img = ...;
...
vector<Mat> roiVector;
for(vector<vector<Point> >::iterator it=contours.begin(); it<contours.end(); it++) {
if (boundingRect( (*it)).area()>minArea) {
roiVector.push_back(img(boundingRect(*it)));
}
}
cv::boundingRect() takes a vector of Points and returns a cv::Rect. Initializing a Mat myRoi = img(myRect) gives you a pointer to that part of the image (so modifying myRoi will ALSO modify img).
See more here.