My attempt considers that painted nails have high brightness and fingers have orientation almost similar.
My code can extract images from single nails but it get some noise too. Anyway result could be used as input for a classifier.
As threshold I used the absolute peak for Brightness. I removed some noise using a threshold on Saturation in the middle of 2 biggest peaks
For example starting from this image
A get histogram for HSV And get threshold for brightness and saturation (you can calculate it automatically if you have a peak detector)
This is the binary image after some cleaning
Some contour and rotated rect to get orientation and a ROI for the single detection
Finally sort the detections, rotate and extract single images. From quadrant 1
From quadrant 2
here the full code. (Get FillHoles
from @theodore answer here: http://answers.opencv.org/question/74...
#define CL_BLU cv::Scalar(255, 0, 0 )
#define CL_GREEN cv::Scalar(0, 255, 0 )
#define CL_RED cv::Scalar(0, 0, 255)
#define CL_BLACK cv::Scalar(0, 0, 0 )
#define CL_CYAN cv::Scalar(255, 255, 0 )
#define CL_MAGENTA cv::Scalar(255, 0, 255)
#define CL_YELLOW cv::Scalar(0, 255, 255)
#define CL_WHITE cv::Scalar(255, 255, 255)
void Nails()
{
cv::Scalar cl;
cv::Mat src, dst, src_hsv, src_saturation, src_bright, bw, dist;
src = cv::imread("../img/nails.jpg");
src.copyTo(dst);
vector<cv::Mat> hsv_planes;
cv::cvtColor(src, src_hsv, cv::COLOR_BGR2HSV);
cv::split(src_hsv, hsv_planes);
src_saturation = hsv_planes[1];
src_bright = hsv_planes[2];
int thBright = 186, thSat=180;
cv::inRange(src_hsv, cv::Scalar(0, thSat, thBright), cv::Scalar(180, 255, 255), bw);
FillHoles(bw, bw);
cv::bitwise_not(bw, bw);
Morph(bw, bw, cv::MORPH_CLOSE, cv::MORPH_RECT, 3);
Morph(bw, bw, cv::MORPH_OPEN, cv::MORPH_RECT, 1);
cv::imshow("After TH1", bw);
cv::imwrite("../img/bw.jpg", dst);
// Get external contours ignoring holes
std::vector<std::vector<cv::Point> > contours;
cv::findContours(bw, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
cv::RotatedRect rr;
map <int, vector<cv::RotatedRect>> quadrant;
vector<cv::Scalar> quadrantColor({ CL_MAGENTA, CL_YELLOW, CL_CYAN, CL_GREEN });
double area,minArea = 0.005*bw.cols*bw.rows;
cv::Point2f rect_points[4];
// draw contours, select them and group by orientation
for (int i = 0; i < contours.size(); i++)
{
cv::drawContours(dst, contours, i, CL_WHITE, 1);
//filter out small detections
if (contours[i].size() < 10) continue;
area = cv::contourArea(contours[i]);
if (area < minArea) continue;
// Get a rotated rect around the detection
rr = cv::fitEllipse(contours[i]);
// group detections by orientation in quadrant
// because there is a good chance that all fingers
// have same orientation.
int q = floor(rr.angle / 90);
quadrant[q].push_back(rr);
// Draw rotated rect around the detection
cl = quadrantColor[q];
rr.points(rect_points);
for (int j = 0; j < 4; j++)
cv::line(dst, rect_points[j], rect_points[(j + 1) % 4], cl, 2, 8);
cv::putText(dst, "Q" + to_string(q)+"C" + to_string(i), rr.center - cv::Point2f(3, 0), cv::FONT_HERSHEY_PLAIN, 1, CL_BLACK, 2,CV_AA);
}
// Get quadrant with more detections count
// you could consider this as related to fingers
int cnt = 0;
int populatedQuadrant ...
(more)
i think about an optimal solution. could you provide some more pictures?
If you simple type "blue nail polish" (or any other color) in google images, you'll get a bunch :) Thanks for helping me
@sturkmen, any ideas yet?
I've added some more pictures :) Still don't know how to do it..
You will need to apply a bag of visual words approach on fingernail objects. A tutorial can be found on GilLevi's blog.
Thanks for your answer, but what are exactly are fingernail objects? Just the figernails photoshopped out on a white background? I just don't know how these things are done. Could you write out some steps (e.g. which image as input/output to which algorithm, etc) which i have to follow. Thanks in advance