Related
I'm trying to write a Maya plugin that recreates a 2d drawing of bones in UV space to 3D space. I'm starting with a simple plane with this image:
What I need is two find the circles and create a hierarchy.
I tried Nuzhny approach but I'm getting horizontal lines like:
My code:
Mat image;
image = imread("c:/pjs/sk.jpg"); // Read the file
cv::Mat hsv_image;
cv::cvtColor(image, hsv_image, cv::COLOR_BGR2HSV);
cv::Mat lower_red_hue_range;
cv::Mat upper_red_hue_range;
cv::Mat white_hue_range;
//Separate the lines and circles
cv::inRange(hsv_image, cv::Scalar(0, 100, 100), cv::Scalar(10, 255, 255), lower_red_hue_range);
cv::inRange(hsv_image, cv::Scalar(160, 100, 100), cv::Scalar(179, 255, 255), upper_red_hue_range);
cv::inRange(hsv_image, cv::Scalar(0, 0, 20), cv::Scalar(0, 0, 255), white_hue_range);
cv::Mat red_hue_image;
cv::addWeighted(lower_red_hue_range, 1.0, upper_red_hue_range, 1.0, 0.0, red_hue_image);
cv::GaussianBlur(red_hue_image, red_hue_image, cv::Size(9, 9), 2, 2);
//Identify circles
std::vector<cv::Vec3f> circles;
cv::HoughCircles(red_hue_image, circles, HOUGH_GRADIENT, 1, red_hue_image.rows / 8, 100, 20, 0, 0);
if (circles.size() == 0) std::exit(-1);
for (size_t current_circle = 0; current_circle < circles.size(); ++current_circle) {
cv::Point center(std::round(circles[current_circle][0]), std::round(circles[current_circle][1]));
int radius = std::round(circles[current_circle][2]);
cv::circle(image, center, radius, cv::Scalar(0, 255, 0), 5);
}
//Get the contours
cv::threshold(white_hue_range, white_hue_range, 11, 255, cv::THRESH_BINARY);
cv::Mat element = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3));
element = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(20, 20));
cv::dilate(white_hue_range, white_hue_range, element);
cv::dilate(white_hue_range, white_hue_range, element);
cv::erode(white_hue_range, white_hue_range, element);
cv::erode(white_hue_range, white_hue_range, element);
element = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(5, 5));
cv::dilate(white_hue_range, white_hue_range, element);
Mat gray;
gray = white_hue_range;
Canny(gray, gray, 40, 100, 7);
/// Find contours
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
RNG rng(12345);
findContours(gray, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
/// Draw contours
Mat drawing = Mat::zeros(gray.size(), CV_8UC3);
for (int i = 0; i < contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
}
//Get the lines
vector<vector<Point2f> > lines;
vector<Point> approx;
for (unsigned int i = 0; i < contours.size(); i++)
{
if (contours[i].size() > 4) {
//cv::Rect box = cv::fitEllipse(contours[i]);
cv::RotatedRect box = cv::fitEllipseAMS(contours[i]);
cv::Point2f pts[4];
box.points(pts);
vector<cv::Point2f> line_pts;
line_pts.resize(2);
line_pts[0] = (pts[0] + pts[1]) / 2;
line_pts[1] = (pts[2] + pts[3]) / 2;
lines.push_back(line_pts);
}
}
for (int i = 0; i < lines.size(); i++)
{
line(image, lines[i].at(0), lines[i].at(1), 128, 4, LINE_8, 0);
}
imshow("Result window", image);
cvtColor to HSV.
inRange(redFrom, redTo) + findContours to find red circles.
inRange(whiteFrom, whiteTo) + findContours to find white lines.
Line contour to line:
cv::RotatedRect box = cv::fitEllipse(line_contours[i]);
cv::Point2f pts[4];
box.points(pts);
cv::Point2f line_pts[2];
line_pts[0] = (pts[0] + pts[3]) / 2;
line_pts[1] = (pts[1] + pts[2]) / 2;
Nested loops to find a nearest circle for each line point.
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);
What I'm basically trying to do is blur an image, and combine it back with the orignal, so that only certain areas in the original image are blurred (the face should be blurred).
My general idea was to mask the parts in the original Iwant to have blurred, then blur the original as a copy and "merge" them together again.
To a certain extend this also worked.
My images:
(1) Original
(2) Original with parts that should be blurred
(3) Blurred
My C++ code that creates these images:
int main(void) {
cv::Mat srcImage = cv::imread(path);
srcImage.convertTo(srcImage, CV_32FC3, 1.0/255.0);
Mat _mask;
Mat img_gray;
cv::Scalar white = cv::Scalar(255, 255, 255);
cv::Scalar black = cv::Scalar(0, 0, 0);
cv::cvtColor(srcImage, img_gray, cv::COLOR_BGR2GRAY);
img_gray.convertTo(_mask, CV_32FC1);
// face
cv::circle(_mask, cv::Point(430, 350), 200, black, -1, 8, 0);
// eyes
cv::circle(_mask, cv::Point(502, 260), 27, white, -1, 8, 0);
cv::circle(_mask, cv::Point(390, 260), 27, white, -1, 8, 0);
// mouth
cv::ellipse(_mask, cv::Point(440, 390), cv::Point(60, 25), 0, 0, 360, white, -1, 8, 0);
cv::threshold(1.0-_mask, _mask, 0.9, 1.0, cv::THRESH_BINARY_INV);
cv::GaussianBlur(_mask,_mask,Size(21,21),11.0);
cv::Mat res;
cv::Mat bg = Mat(srcImage.size(), CV_32FC3);
bg = cv::Scalar(1.0, 1.0 ,1.0);
vector<Mat> ch_img(3);
vector<Mat> ch_bg(3);
cv::split(srcImage, ch_img);
cv::split(bg, ch_bg);
ch_img[0] = ch_img[0].mul(_mask) + ch_bg[0].mul(1.0 - _mask);
ch_img[1] = ch_img[1].mul(_mask) + ch_bg[1].mul(1.0 - _mask);
ch_img[2] = ch_img[2].mul(_mask) + ch_bg[2].mul(1.0 - _mask);
cv::merge(ch_img, res);
cv::merge(ch_bg, bg);
// original but with white mask
res.convertTo(res, CV_8UC3, 255.0);
imwrite("original_with_mask.jpg", res);
// blur original image
cv::Mat blurredImage;
bilateralFilter(srcImage, blurredImage, 10, 20, 5);
GaussianBlur(srcImage, blurredImage, Size(19, 19), 0, 0);
blurredImage.convertTo(blurredImage, CV_8UC3, 255.0);
imwrite("blurred.jpg", blurredImage);
cv::Mat maskedImage;
maskedImage = Mat(srcImage.size(), CV_32FC3);
// now combine blurred image and original using mask
// this fails
cv::bitwise_and(blurredImage, _mask, maskedImage);
cv::imwrite("masked.jpg", maskedImage);
}
My problem is that cv::bitwise_and(blurredImage, _mask, maskedImage); fails with
OpenCV Error: Sizes of input arguments do not match (The operation is neither 'array op array' (where arrays have the same size and type), nor 'array op scalar', nor 'scalar op array') in binary_op
Probably because _mask is a single channel image and blurredImage and maskedImage are 3-channel images.
How can I combine the images I got so that the currently white areas in image (2) are blurred using a transparent mask with "soft" edges?
Instead of float conversion you can just use the linearcombination of byte channel values. See
int main(int argc, char* argv[])
{
cv::Mat srcImage = cv::imread("C:/StackOverflow/Input/transparentMaskInput.jpg");
// blur whole image
cv::Mat blurredImage;
//cv::bilateralFilter(srcImage, blurredImage, 10, 20, 5); // use EITHER bilateral OR Gaússian filter
cv::GaussianBlur(srcImage, blurredImage, cv::Size(19, 19), 0, 0);
// create mask
cv::Scalar white = cv::Scalar(255, 255, 255);
cv::Scalar black = cv::Scalar(0, 0, 0);
cv::Mat mask = cv::Mat::zeros(srcImage.size(), CV_8UC1);
// face
cv::circle(mask, cv::Point(430, 350), 200, black, -1, 8, 0);
// eyes
cv::circle(mask, cv::Point(502, 260), 27, white, -1, 8, 0);
cv::circle(mask, cv::Point(390, 260), 27, white, -1, 8, 0);
// mouth
cv::ellipse(mask, cv::Point(440, 390), cv::Point(60, 25), 0, 0, 360, white, -1, 8, 0);
cv::GaussianBlur(mask, mask, cv::Size(21, 21), 11.0);
// byte inversion:
cv::Mat invertedMask = 255 - mask; // instead of inversion you could just draw the "face" black on a white background!
cv::Mat outputImage = cv::Mat(srcImage.size(), srcImage.type());
// for each pixel, merge blurred and original image regarding the blur-mask
for (int y = 0; y < outputImage.rows; ++y)
for (int x = 0; x < outputImage.cols; ++x)
{
cv::Vec3b pixelOrig = srcImage.at<cv::Vec3b>(y, x);
cv::Vec3b pixelBlur = blurredImage.at<cv::Vec3b>(y, x);
float blurVal = invertedMask.at<unsigned char>(y, x)/255.0f; // value between 0 and 1: zero means 100% orig image, one means 100% blurry image
cv::Vec3b pixelOut = blurVal * pixelBlur + (1.0f - blurVal)* pixelOrig;
outputImage.at<cv::Vec3b>(y, x) = pixelOut;
}
cv::imshow("input", srcImage);
cv::imshow("blurred", blurredImage);
cv::imshow("mask", mask);
cv::imshow("inverted mask", invertedMask);
cv::imshow("output", outputImage);
return 0;
}
using this input image:
computing this blurred and mask:
resulting in this output, by computing (mask/255) * blur + (1-mask/255)*blur (linear combination):
I define a function to do alphaBlend for two images of CV_8UC3 with a mask of CV_8UC1 in OpenCV:
//! 2018.01.16 13:54:39 CST
//! 2018.01.16 14:43:26 CST
void alphaBlend(Mat& img1, Mat&img2, Mat& mask, Mat& blended){
// Blend img1 and img2 (of CV_8UC3) with mask (CV_8UC1)
assert(img1.size() == img2.size() && img1.size() == mask.size());
blended = cv::Mat(img1.size(), img1.type());
for (int y = 0; y < blended.rows; ++y){
for (int x = 0; x < blended.cols; ++x){
float alpha = mask.at<unsigned char>(y, x)/255.0f;
blended.at<cv::Vec3b>(y,x) = alpha*img1.at<cv::Vec3b>(y,x) + (1-alpha)*img2.at<cv::Vec3b>(y,x);
}
}
}
Then, it's easy to do alpha bend on the images, just call alphaBlend(...). Here is an example:
#include <opencv2/opencv.hpp>
using namespace cv;
//! 2018.01.16 13:54:39 CST
//! 2018.01.16 14:43:26 CST
void alphaBlend(Mat& img1, Mat&img2, Mat& mask, Mat& blended){
// Blend img1 and img2 (of CV_8UC3) with mask (CV_8UC1)
assert(img1.size() == img2.size() && img1.size() == mask.size());
blended = cv::Mat(img1.size(), img1.type());
for (int y = 0; y < blended.rows; ++y){
for (int x = 0; x < blended.cols; ++x){
float alpha = mask.at<unsigned char>(y, x)/255.0f;
blended.at<cv::Vec3b>(y,x) = alpha*img1.at<cv::Vec3b>(y,x) + (1-alpha)*img2.at<cv::Vec3b>(y,x);
}
}
}
Mat createMask(Size sz){
// create mask
cv::Mat mask = cv::Mat::zeros(sz, CV_8UC1);
// white and black
cv::Scalar white = cv::Scalar(255, 255, 255);
cv::Scalar black = cv::Scalar(0, 0, 0);
// face
cv::circle(mask, cv::Point(430, 350), 200, black, -1, 8, 0);
// eyes
cv::circle(mask, cv::Point(502, 260), 27, white, -1, 8, 0);
cv::circle(mask, cv::Point(390, 260), 27, white, -1, 8, 0);
// mouth
cv::ellipse(mask, cv::Point(440, 390), cv::Point(60, 25), 0, 0, 360, white, -1, 8, 0);
// Blur
cv::GaussianBlur(mask, mask, cv::Size(21, 21), 11.0);
return mask;
}
int main(){
cv::Mat img = cv::imread("img04.jpg");
// blur whole image
cv::Mat blured;
//cv::bilateralFilter(img, blured, 10, 20, 5); // use EITHER bilateral OR Gaússian filter
cv::GaussianBlur(img, blured, cv::Size(19, 19), 0, 0);
// Create the mask
Mat mask = createMask(img.size());
Mat mask_inv = 255 - mask;
// Alpha blend
Mat blended1, blended2;
alphaBlend(img, blured, mask, blended1);
alphaBlend(img, blured, mask_inv, blended2);
// Display
cv::imshow("source", img);
cv::imshow("blured", blured);
cv::imshow("mask", mask);
cv::imshow("mask_inv", mask_inv);
cv::imshow("blended1", blended1);
cv::imshow("blended2", blended2);
cv::waitKey();
return 0;
}
Source:
Blured:
Mask1:
AlphaBlend 1:
Mask 2:
AlphaBlend 2:
Some useful links:
Alpha Blending in OpenCV C++ : Combining 2 images with transparent mask in opencv
Alpha Blending in OpenCV Python:
Gradient mask blending in opencv python
Probably because _mask is a single channel image and blurredImage and
maskedImage are 3-channel images.
Put this before calling the cv::bitwise_and:
P.S if you do not want to alter your mask becuase you want to use it in another place just do it in a temporary variable:
cv::Mat _mask_temp;
cv::cvtColor(_mask,_mask_temp,cv::COLOR_GRAY2BGR);
cv::bitwise_and(blurredImage, _mask_temp, maskedImage);
_mask_temp.release(); // just in case you do not want it anymore to be in your memory(optional)
EDIT (another problem):
The mask is 32F while the image is 8U. So, you need this:
cv::cvtColor(_mask,_mask,cv::COLOR_GRAY2BGR);
_mask.convertTo(_mask, CV_8UC3);
I want to use convexity defects of a human hand as input to a classifier. I want to do this to detect hand gestures (sign language alphabet). Can someone please help me.
The code below is available on the opencv documentation. I want to know how to use this for my the purpose stated above.
// Data for visual representation
int width = 512, height = 512;
Mat image = Mat::zeros(height, width, CV_8UC3);
// Set up training data
float labels[4] = { 1.0, -1.0, -1.0, -1.0 };
Mat labelsMat(4, 1, CV_32FC1, labels);
// Storing as Mat objects of floats
float trainingData[4][2] = { { 501, 10 }, { 255, 10 }, { 501, 255 }, { 10, 501 } };
Mat trainingDataMat(4, 2, CV_32FC1, trainingData);
// Set up SVM's parameters
CvSVMParams params;
params.svm_type = CvSVM::C_SVC;
params.kernel_type = CvSVM::LINEAR;
params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
// Train the SVM
CvSVM SVM;
SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);
Vec3b green(0, 255, 0), blue(255, 0, 0);
// Show the decision regions given by the SVM
for (int i = 0; i < image.rows; ++i)
for (int j = 0; j < image.cols; ++j)
{
Mat sampleMat = (Mat_<float>(1, 2) << j, i);
float response = SVM.predict(sampleMat);
if (response == 1)
image.at<Vec3b>(i, j) = green;
else if (response == -1)
image.at<Vec3b>(i, j) = blue;
}
// Show the training data
int thickness = -1;
int lineType = 8;
circle(image, Point(501, 10), 5, Scalar(0, 0, 0), thickness, lineType);
circle(image, Point(255, 10), 5, Scalar(255, 255, 255), thickness, lineType);
circle(image, Point(501, 255), 5, Scalar(255, 255, 255), thickness, lineType);
circle(image, Point(10, 501), 5, Scalar(255, 255, 255), thickness, lineType);
// Show support vectors
thickness = 2;
lineType = 8;
int c = SVM.get_support_vector_count();
for (int i = 0; i < c; ++i)
{
const float* v = SVM.get_support_vector(i);
circle(image, Point((int)v[0], (int)v[1]), 6, Scalar(128, 128, 128), thickness, lineType);
}
imwrite("result.png", image); // save the image
imshow("SVM Simple Example", image); // show it to the user
waitKey(0);
}
I'm trying to extract from an image only the contours which have a specific size.
I process like this
int offsetX ;
int offsetY ;
//here: read original image as 8UC3
cv::Mat original = cv::imread("0.png");
Mat imgx=original.clone();
cv::imshow("original", original);
cvtColor(imgx,imgx,CV_BGR2GRAY);
Mat thresh;
vector<Vec4i> hierarchy;
RNG rng(12345);
vector < vector<Point> > contours;
adaptiveThreshold(imgx, thresh, 255, 1, 1, 31, 2);
findContours(thresh, contours, CV_RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
Mat drawing = Mat::zeros(thresh.size(), CV_8UC3);
cout << "drawing "<<drawing.type()<<endl;
cv::Mat image = cv::Mat(original.rows, original.cols, original.type());
image.setTo(cv::Scalar::all(255));
for (size_t i = 0; i < contours.size(); i++)
{
vector < Point > cnt = contours[i];
if (contourArea(cnt) > 0)
{
Rect rec = boundingRect(cnt);
if ((rec.height > 20 ) &&(3.5*rec.height>rec.width)&& (rec.width>15)/*&& (rec.width<40)*/)
{
cout<<rec.x<<" "<<rec.y<<endl;
offsetX=rec.x;
offsetY=rec.y;
Mat roi = original(rec);
int width = roi.cols;
int height = roi.rows;
cout <<"h= "<<height<<" w= "<<width<<endl;
cv::Rect characterLocation;
if(height>35)
characterLocation = cv::Rect(offsetX+3, offsetY, width, height);
else
characterLocation = cv::Rect(offsetX, offsetY, width, height);
original(characterLocation).copyTo(image(characterLocation));
imshow("jihedddd",roi);
imwrite("xxxxxx.png",roi);
Mat stagedImage;
Mat img;
Scalar color = Scalar(255, 255, 255);
drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
imshow("Contours", drawing);
waitKey();
GaussianBlur(stagedImage, img, Size(5, 5), 2, 2);
medianBlur(img, stagedImage, 3);
Mat copy = original.clone();
rectangle(copy, Point(rec.x, rec.y),
Point(rec.x + rec.width, rec.y + rec.height),
CV_RGB(0x00,0x00,0xff), 3);
cv::imshow("char copied", image);
}
}
}
medianBlur(image,image,3);
cv::imshow("char copied", image);
cv::imwrite("characterC_result.tiff ", image);
cv::waitKey();
But when I run this code I have an error
opencv error :assertion failed
This is an example of image which give me the error.
you have to be careful with this:
characterLocation = cv::Rect(offsetX+3, offsetY, width, height);
if width is the imagewidth, you are out of bounds here. you either have to subtract x from width, or crop the rect to the image borders:
// get the Rect for the original image:
cv::Rect borders(Point(0,0), image.size());
// crop to the legal size:
characterLocation = cv::Rect(offsetX+3, offsetY, width, height) & borders;