diff --git a/src/computervision/BackgroundRemover.cpp b/src/computervision/BackgroundRemover.cpp index fc78fea..57a58b8 100644 --- a/src/computervision/BackgroundRemover.cpp +++ b/src/computervision/BackgroundRemover.cpp @@ -1,58 +1,59 @@ #include "BackgroundRemover.h" -#include"opencv2\opencv.hpp" /* Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti */ +namespace computervision +{ + BackgroundRemover::BackgroundRemover(void) { + background; + calibrated = false; + } -BackgroundRemover::BackgroundRemover(void) { - background; - calibrated = false; -} + void BackgroundRemover::calibrate(Mat input) { + cvtColor(input, background, CV_BGR2GRAY); + calibrated = true; + } -void BackgroundRemover::calibrate(Mat input) { - cvtColor(input, background, CV_BGR2GRAY); - calibrated = true; -} + Mat BackgroundRemover::getForeground(Mat input) { + Mat foregroundMask = getForegroundMask(input); -Mat BackgroundRemover::getForeground(Mat input) { - Mat foregroundMask = getForegroundMask(input); + //imshow("foregroundMask", foregroundMask); - //imshow("foregroundMask", foregroundMask); + Mat foreground; + input.copyTo(foreground, foregroundMask); - Mat foreground; - input.copyTo(foreground, foregroundMask); + return foreground; + } - return foreground; -} + Mat BackgroundRemover::getForegroundMask(Mat input) { + Mat foregroundMask; -Mat BackgroundRemover::getForegroundMask(Mat input) { - Mat foregroundMask; + if (!calibrated) { + foregroundMask = Mat::zeros(input.size(), CV_8UC1); + return foregroundMask; + } + + cvtColor(input, foregroundMask, CV_BGR2GRAY); + + removeBackground(foregroundMask, background); - if (!calibrated) { - foregroundMask = Mat::zeros(input.size(), CV_8UC1); return foregroundMask; } - cvtColor(input, foregroundMask, CV_BGR2GRAY); + void BackgroundRemover::removeBackground(Mat input, Mat background) { + int thresholdOffset = 10; - removeBackground(foregroundMask, background); + for (int i = 0; i < input.rows; i++) { + for (int j = 0; j < input.cols; j++) { + uchar framePixel = input.at(i, j); + uchar bgPixel = background.at(i, j); - return foregroundMask; -} - -void BackgroundRemover::removeBackground(Mat input, Mat background) { - int thresholdOffset = 10; - - for (int i = 0; i < input.rows; i++) { - for (int j = 0; j < input.cols; j++) { - uchar framePixel = input.at(i, j); - uchar bgPixel = background.at(i, j); - - if (framePixel >= bgPixel - thresholdOffset && framePixel <= bgPixel + thresholdOffset) - input.at(i, j) = 0; - else - input.at(i, j) = 255; + if (framePixel >= bgPixel - thresholdOffset && framePixel <= bgPixel + thresholdOffset) + input.at(i, j) = 0; + else + input.at(i, j) = 255; + } } } } \ No newline at end of file diff --git a/src/computervision/BackgroundRemover.h b/src/computervision/BackgroundRemover.h index 7eeea38..2c49932 100644 --- a/src/computervision/BackgroundRemover.h +++ b/src/computervision/BackgroundRemover.h @@ -1,24 +1,25 @@ #pragma once - -#include - +#include"opencv2\opencv.hpp" +#include /* Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti */ - +namespace computervision +{ using namespace cv; using namespace std; -class BackgroundRemover { -public: - BackgroundRemover(void); - void calibrate(Mat input); - Mat BackgroundRemover::getForeground(Mat input); + class BackgroundRemover { + public: + BackgroundRemover(void); + void calibrate(Mat input); + Mat getForeground(Mat input); -private: - Mat background; - bool calibrated = false; + private: + Mat background; + bool calibrated = false; - Mat getForegroundMask(Mat input); - void BackgroundRemover::removeBackground(Mat input, Mat background); -}; \ No newline at end of file + Mat getForegroundMask(Mat input); + void removeBackground(Mat input, Mat background); + }; +} \ No newline at end of file diff --git a/src/computervision/FaceDetector.cpp b/src/computervision/FaceDetector.cpp index a32e875..ba80c5e 100644 --- a/src/computervision/FaceDetector.cpp +++ b/src/computervision/FaceDetector.cpp @@ -1,51 +1,53 @@ #include "FaceDetector.h" -#include"opencv2\opencv.hpp" + /* Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti */ +namespace computervision +{ + Rect getFaceRect(Mat input); -Rect getFaceRect(Mat input); + String faceClassifierFileName = "../res/haarcascade_frontalface_alt.xml"; + CascadeClassifier faceCascadeClassifier; -String faceClassifierFileName = "../res/haarcascade_frontalface_alt.xml"; -CascadeClassifier faceCascadeClassifier; - -FaceDetector::FaceDetector(void) { - if (!faceCascadeClassifier.load(faceClassifierFileName)) - throw runtime_error("can't load file " + faceClassifierFileName); -} - -void FaceDetector::removeFaces(Mat input, Mat output) { - vector faces; - Mat frameGray; - - cvtColor(input, frameGray, CV_BGR2GRAY); - equalizeHist(frameGray, frameGray); - - faceCascadeClassifier.detectMultiScale(frameGray, faces, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(120, 120)); - - for (size_t i = 0; i < faces.size(); i++) { - rectangle( - output, - Point(faces[i].x, faces[i].y), - Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height), - Scalar(0, 0, 0), - -1 - ); + FaceDetector::FaceDetector(void) { + if (!faceCascadeClassifier.load(faceClassifierFileName)) + throw runtime_error("can't load file " + faceClassifierFileName); } -} -Rect getFaceRect(Mat input) { - vector faceRectangles; - Mat inputGray; + void FaceDetector::removeFaces(Mat input, Mat output) { + vector faces; + Mat frameGray; - cvtColor(input, inputGray, CV_BGR2GRAY); - equalizeHist(inputGray, inputGray); + cvtColor(input, frameGray, CV_BGR2GRAY); + equalizeHist(frameGray, frameGray); - faceCascadeClassifier.detectMultiScale(inputGray, faceRectangles, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(120, 120)); + faceCascadeClassifier.detectMultiScale(frameGray, faces, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(120, 120)); - if (faceRectangles.size() > 0) - return faceRectangles[0]; - else - return Rect(0, 0, 1, 1); + for (size_t i = 0; i < faces.size(); i++) { + rectangle( + output, + Point(faces[i].x, faces[i].y), + Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height), + Scalar(0, 0, 0), + -1 + ); + } + } + + Rect getFaceRect(Mat input) { + vector faceRectangles; + Mat inputGray; + + cvtColor(input, inputGray, CV_BGR2GRAY); + equalizeHist(inputGray, inputGray); + + faceCascadeClassifier.detectMultiScale(inputGray, faceRectangles, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(120, 120)); + + if (faceRectangles.size() > 0) + return faceRectangles[0]; + else + return Rect(0, 0, 1, 1); + } } \ No newline at end of file diff --git a/src/computervision/FaceDetector.h b/src/computervision/FaceDetector.h index 5e02ab1..e9ae7e2 100644 --- a/src/computervision/FaceDetector.h +++ b/src/computervision/FaceDetector.h @@ -1,7 +1,7 @@ #pragma once - -#include - +#include"opencv2\opencv.hpp" +#include +#include /* Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti */ @@ -9,8 +9,11 @@ using namespace cv; using namespace std; -class FaceDetector { -public: - FaceDetector(void); - void removeFaces(Mat input, Mat output); -}; \ No newline at end of file +namespace computervision +{ + class FaceDetector { + public: + FaceDetector(void); + void removeFaces(Mat input, Mat output); + }; +} \ No newline at end of file diff --git a/src/computervision/FingerCount.cpp b/src/computervision/FingerCount.cpp index 3400a41..9f0f577 100644 --- a/src/computervision/FingerCount.cpp +++ b/src/computervision/FingerCount.cpp @@ -12,277 +12,280 @@ #define BOUNDING_RECT_FINGER_SIZE_SCALING 0.3 #define BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING 0.05 -FingerCount::FingerCount(void) { - color_blue = Scalar(255, 0, 0); - color_green = Scalar(0, 255, 0); - color_red = Scalar(0, 0, 255); - color_black = Scalar(0, 0, 0); - color_white = Scalar(255, 255, 255); - color_yellow = Scalar(0, 255, 255); - color_purple = Scalar(255, 0, 255); -} - -Mat FingerCount::findFingersCount(Mat input_image, Mat frame) { - Mat contours_image = Mat::zeros(input_image.size(), CV_8UC3); - - // check if the source image is good - if (input_image.empty()) - return contours_image; - - // we work only on the 1 channel result, since this function is called inside a loop we are not sure that this is always the case - if (input_image.channels() != 1) - return contours_image; - - vector> contours; - vector hierarchy; - - findContours(input_image, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); - - // we need at least one contour to work - if (contours.size() <= 0) - return contours_image; - - // find the biggest contour (let's suppose it's our hand) - int biggest_contour_index = -1; - double biggest_area = 0.0; - - for (int i = 0; i < contours.size(); i++) { - double area = contourArea(contours[i], false); - if (area > biggest_area) { - biggest_area = area; - biggest_contour_index = i; - } +namespace computervision +{ + FingerCount::FingerCount(void) { + color_blue = Scalar(255, 0, 0); + color_green = Scalar(0, 255, 0); + color_red = Scalar(0, 0, 255); + color_black = Scalar(0, 0, 0); + color_white = Scalar(255, 255, 255); + color_yellow = Scalar(0, 255, 255); + color_purple = Scalar(255, 0, 255); } - if (biggest_contour_index < 0) - return contours_image; + Mat FingerCount::findFingersCount(Mat input_image, Mat frame) { + Mat contours_image = Mat::zeros(input_image.size(), CV_8UC3); - // find the convex hull object for each contour and the defects, two different data structure are needed by the OpenCV api - vector hull_points; - vector hull_ints; + // check if the source image is good + if (input_image.empty()) + return contours_image; - // for drawing the convex hull and for finding the bounding rectangle - convexHull(Mat(contours[biggest_contour_index]), hull_points, true); + // we work only on the 1 channel result, since this function is called inside a loop we are not sure that this is always the case + if (input_image.channels() != 1) + return contours_image; - // for finding the defects - convexHull(Mat(contours[biggest_contour_index]), hull_ints, false); + vector> contours; + vector hierarchy; - // we need at least 3 points to find the defects - vector defects; - if (hull_ints.size() > 3) - convexityDefects(Mat(contours[biggest_contour_index]), hull_ints, defects); - else - return contours_image; + findContours(input_image, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); - // we bound the convex hull - Rect bounding_rectangle = boundingRect(Mat(hull_points)); + // we need at least one contour to work + if (contours.size() <= 0) + return contours_image; - // we find the center of the bounding rectangle, this should approximately also be the center of the hand - Point center_bounding_rect( - (bounding_rectangle.tl().x + bounding_rectangle.br().x) / 2, - (bounding_rectangle.tl().y + bounding_rectangle.br().y) / 2 - ); + // find the biggest contour (let's suppose it's our hand) + int biggest_contour_index = -1; + double biggest_area = 0.0; - // we separate the defects keeping only the ones of intrest - vector start_points; - vector far_points; - - for (int i = 0; i < defects.size(); i++) { - start_points.push_back(contours[biggest_contour_index][defects[i].val[0]]); - - // filtering the far point based on the distance from the center of the bounding rectangle - if (findPointsDistance(contours[biggest_contour_index][defects[i].val[2]], center_bounding_rect) < bounding_rectangle.height * BOUNDING_RECT_FINGER_SIZE_SCALING) - far_points.push_back(contours[biggest_contour_index][defects[i].val[2]]); - } - - // we compact them on their medians - vector filtered_start_points = compactOnNeighborhoodMedian(start_points, bounding_rectangle.height * BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING); - vector filtered_far_points = compactOnNeighborhoodMedian(far_points, bounding_rectangle.height * BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING); - - // now we try to find the fingers - vector filtered_finger_points; - - if (filtered_far_points.size() > 1) { - vector finger_points; - - for (int i = 0; i < filtered_start_points.size(); i++) { - vector closest_points = findClosestOnX(filtered_far_points, filtered_start_points[i]); - - if (isFinger(closest_points[0], filtered_start_points[i], closest_points[1], LIMIT_ANGLE_INF, LIMIT_ANGLE_SUP, center_bounding_rect, bounding_rectangle.height * BOUNDING_RECT_FINGER_SIZE_SCALING)) - finger_points.push_back(filtered_start_points[i]); + for (int i = 0; i < contours.size(); i++) { + double area = contourArea(contours[i], false); + if (area > biggest_area) { + biggest_area = area; + biggest_contour_index = i; + } } - if (finger_points.size() > 0) { + if (biggest_contour_index < 0) + return contours_image; - // we have at most five fingers usually :) - while (finger_points.size() > 5) - finger_points.pop_back(); + // find the convex hull object for each contour and the defects, two different data structure are needed by the OpenCV api + vector hull_points; + vector hull_ints; - // filter out the points too close to each other - for (int i = 0; i < finger_points.size() - 1; i++) { - if (findPointsDistanceOnX(finger_points[i], finger_points[i + 1]) > bounding_rectangle.height * BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING * 1.5) - filtered_finger_points.push_back(finger_points[i]); + // for drawing the convex hull and for finding the bounding rectangle + convexHull(Mat(contours[biggest_contour_index]), hull_points, true); + + // for finding the defects + convexHull(Mat(contours[biggest_contour_index]), hull_ints, false); + + // we need at least 3 points to find the defects + vector defects; + if (hull_ints.size() > 3) + convexityDefects(Mat(contours[biggest_contour_index]), hull_ints, defects); + else + return contours_image; + + // we bound the convex hull + Rect bounding_rectangle = boundingRect(Mat(hull_points)); + + // we find the center of the bounding rectangle, this should approximately also be the center of the hand + Point center_bounding_rect( + (bounding_rectangle.tl().x + bounding_rectangle.br().x) / 2, + (bounding_rectangle.tl().y + bounding_rectangle.br().y) / 2 + ); + + // we separate the defects keeping only the ones of intrest + vector start_points; + vector far_points; + + for (int i = 0; i < defects.size(); i++) { + start_points.push_back(contours[biggest_contour_index][defects[i].val[0]]); + + // filtering the far point based on the distance from the center of the bounding rectangle + if (findPointsDistance(contours[biggest_contour_index][defects[i].val[2]], center_bounding_rect) < bounding_rectangle.height * BOUNDING_RECT_FINGER_SIZE_SCALING) + far_points.push_back(contours[biggest_contour_index][defects[i].val[2]]); + } + + // we compact them on their medians + vector filtered_start_points = compactOnNeighborhoodMedian(start_points, bounding_rectangle.height * BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING); + vector filtered_far_points = compactOnNeighborhoodMedian(far_points, bounding_rectangle.height * BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING); + + // now we try to find the fingers + vector filtered_finger_points; + + if (filtered_far_points.size() > 1) { + vector finger_points; + + for (int i = 0; i < filtered_start_points.size(); i++) { + vector closest_points = findClosestOnX(filtered_far_points, filtered_start_points[i]); + + if (isFinger(closest_points[0], filtered_start_points[i], closest_points[1], LIMIT_ANGLE_INF, LIMIT_ANGLE_SUP, center_bounding_rect, bounding_rectangle.height * BOUNDING_RECT_FINGER_SIZE_SCALING)) + finger_points.push_back(filtered_start_points[i]); } - if (finger_points.size() > 2) { - if (findPointsDistanceOnX(finger_points[0], finger_points[finger_points.size() - 1]) > bounding_rectangle.height * BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING * 1.5) + if (finger_points.size() > 0) { + + // we have at most five fingers usually :) + while (finger_points.size() > 5) + finger_points.pop_back(); + + // filter out the points too close to each other + for (int i = 0; i < finger_points.size() - 1; i++) { + if (findPointsDistanceOnX(finger_points[i], finger_points[i + 1]) > bounding_rectangle.height * BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING * 1.5) + filtered_finger_points.push_back(finger_points[i]); + } + + if (finger_points.size() > 2) { + if (findPointsDistanceOnX(finger_points[0], finger_points[finger_points.size() - 1]) > bounding_rectangle.height * BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING * 1.5) + filtered_finger_points.push_back(finger_points[finger_points.size() - 1]); + } + else filtered_finger_points.push_back(finger_points[finger_points.size() - 1]); } + } + + // we draw what found on the returned image + drawContours(contours_image, contours, biggest_contour_index, color_green, 2, 8, hierarchy); + polylines(contours_image, hull_points, true, color_blue); + rectangle(contours_image, bounding_rectangle.tl(), bounding_rectangle.br(), color_red, 2, 8, 0); + circle(contours_image, center_bounding_rect, 5, color_purple, 2, 8); + drawVectorPoints(contours_image, filtered_start_points, color_blue, true); + drawVectorPoints(contours_image, filtered_far_points, color_red, true); + drawVectorPoints(contours_image, filtered_finger_points, color_yellow, false); + putText(contours_image, to_string(filtered_finger_points.size()), center_bounding_rect, FONT_HERSHEY_PLAIN, 3, color_purple); + + // and on the starting frame + drawContours(frame, contours, biggest_contour_index, color_green, 2, 8, hierarchy); + circle(frame, center_bounding_rect, 5, color_purple, 2, 8); + drawVectorPoints(frame, filtered_finger_points, color_yellow, false); + putText(frame, to_string(filtered_finger_points.size()), center_bounding_rect, FONT_HERSHEY_PLAIN, 3, color_purple); + + return contours_image; + } + + double FingerCount::findPointsDistance(Point a, Point b) { + Point difference = a - b; + return sqrt(difference.ddot(difference)); + } + + vector FingerCount::compactOnNeighborhoodMedian(vector points, double max_neighbor_distance) { + vector median_points; + + if (points.size() == 0) + return median_points; + + if (max_neighbor_distance <= 0) + return median_points; + + // we start with the first point + Point reference = points[0]; + Point median = points[0]; + + for (int i = 1; i < points.size(); i++) { + if (findPointsDistance(reference, points[i]) > max_neighbor_distance) { + + // the point is not in range, we save the median + median_points.push_back(median); + + // we swap the reference + reference = points[i]; + median = points[i]; + } else - filtered_finger_points.push_back(finger_points[finger_points.size() - 1]); + median = (points[i] + median) / 2; } + + // last median + median_points.push_back(median); + + return median_points; } - // we draw what found on the returned image - drawContours(contours_image, contours, biggest_contour_index, color_green, 2, 8, hierarchy); - polylines(contours_image, hull_points, true, color_blue); - rectangle(contours_image, bounding_rectangle.tl(), bounding_rectangle.br(), color_red, 2, 8, 0); - circle(contours_image, center_bounding_rect, 5, color_purple, 2, 8); - drawVectorPoints(contours_image, filtered_start_points, color_blue, true); - drawVectorPoints(contours_image, filtered_far_points, color_red, true); - drawVectorPoints(contours_image, filtered_finger_points, color_yellow, false); - putText(contours_image, to_string(filtered_finger_points.size()), center_bounding_rect, FONT_HERSHEY_PLAIN, 3, color_purple); - - // and on the starting frame - drawContours(frame, contours, biggest_contour_index, color_green, 2, 8, hierarchy); - circle(frame, center_bounding_rect, 5, color_purple, 2, 8); - drawVectorPoints(frame, filtered_finger_points, color_yellow, false); - putText(frame, to_string(filtered_finger_points.size()), center_bounding_rect, FONT_HERSHEY_PLAIN, 3, color_purple); - - return contours_image; -} - -double FingerCount::findPointsDistance(Point a, Point b) { - Point difference = a - b; - return sqrt(difference.ddot(difference)); -} - -vector FingerCount::compactOnNeighborhoodMedian(vector points, double max_neighbor_distance) { - vector median_points; - - if (points.size() == 0) - return median_points; - - if (max_neighbor_distance <= 0) - return median_points; - - // we start with the first point - Point reference = points[0]; - Point median = points[0]; - - for (int i = 1; i < points.size(); i++) { - if (findPointsDistance(reference, points[i]) > max_neighbor_distance) { - - // the point is not in range, we save the median - median_points.push_back(median); - - // we swap the reference - reference = points[i]; - median = points[i]; - } - else - median = (points[i] + median) / 2; + double FingerCount::findAngle(Point a, Point b, Point c) { + double ab = findPointsDistance(a, b); + double bc = findPointsDistance(b, c); + double ac = findPointsDistance(a, c); + return acos((ab * ab + bc * bc - ac * ac) / (2 * ab * bc)) * 180 / CV_PI; } - // last median - median_points.push_back(median); + bool FingerCount::isFinger(Point a, Point b, Point c, double limit_angle_inf, double limit_angle_sup, Point palm_center, double min_distance_from_palm) { + double angle = findAngle(a, b, c); + if (angle > limit_angle_sup || angle < limit_angle_inf) + return false; - return median_points; -} + // the finger point sohould not be under the two far points + int delta_y_1 = b.y - a.y; + int delta_y_2 = b.y - c.y; + if (delta_y_1 > 0 && delta_y_2 > 0) + return false; -double FingerCount::findAngle(Point a, Point b, Point c) { - double ab = findPointsDistance(a, b); - double bc = findPointsDistance(b, c); - double ac = findPointsDistance(a, c); - return acos((ab * ab + bc * bc - ac * ac) / (2 * ab * bc)) * 180 / CV_PI; -} + // the two far points should not be both under the center of the hand + int delta_y_3 = palm_center.y - a.y; + int delta_y_4 = palm_center.y - c.y; + if (delta_y_3 < 0 && delta_y_4 < 0) + return false; -bool FingerCount::isFinger(Point a, Point b, Point c, double limit_angle_inf, double limit_angle_sup, Point palm_center, double min_distance_from_palm) { - double angle = findAngle(a, b, c); - if (angle > limit_angle_sup || angle < limit_angle_inf) - return false; + double distance_from_palm = findPointsDistance(b, palm_center); + if (distance_from_palm < min_distance_from_palm) + return false; - // the finger point sohould not be under the two far points - int delta_y_1 = b.y - a.y; - int delta_y_2 = b.y - c.y; - if (delta_y_1 > 0 && delta_y_2 > 0) - return false; + // this should be the case when no fingers are up + double distance_from_palm_far_1 = findPointsDistance(a, palm_center); + double distance_from_palm_far_2 = findPointsDistance(c, palm_center); + if (distance_from_palm_far_1 < min_distance_from_palm / 4 || distance_from_palm_far_2 < min_distance_from_palm / 4) + return false; - // the two far points should not be both under the center of the hand - int delta_y_3 = palm_center.y - a.y; - int delta_y_4 = palm_center.y - c.y; - if (delta_y_3 < 0 && delta_y_4 < 0) - return false; + return true; + } - double distance_from_palm = findPointsDistance(b, palm_center); - if (distance_from_palm < min_distance_from_palm) - return false; + vector FingerCount::findClosestOnX(vector points, Point pivot) { + vector to_return(2); - // this should be the case when no fingers are up - double distance_from_palm_far_1 = findPointsDistance(a, palm_center); - double distance_from_palm_far_2 = findPointsDistance(c, palm_center); - if (distance_from_palm_far_1 < min_distance_from_palm / 4 || distance_from_palm_far_2 < min_distance_from_palm / 4) - return false; + if (points.size() == 0) + return to_return; - return true; -} + double distance_x_1 = DBL_MAX; + double distance_1 = DBL_MAX; + double distance_x_2 = DBL_MAX; + double distance_2 = DBL_MAX; + int index_found = 0; -vector FingerCount::findClosestOnX(vector points, Point pivot) { - vector to_return(2); + for (int i = 0; i < points.size(); i++) { + double distance_x = findPointsDistanceOnX(pivot, points[i]); + double distance = findPointsDistance(pivot, points[i]); + + if (distance_x < distance_x_1 && distance_x != 0 && distance <= distance_1) { + distance_x_1 = distance_x; + distance_1 = distance; + index_found = i; + } + } + + to_return[0] = points[index_found]; + + for (int i = 0; i < points.size(); i++) { + double distance_x = findPointsDistanceOnX(pivot, points[i]); + double distance = findPointsDistance(pivot, points[i]); + + if (distance_x < distance_x_2 && distance_x != 0 && distance <= distance_2 && distance_x != distance_x_1) { + distance_x_2 = distance_x; + distance_2 = distance; + index_found = i; + } + } + + to_return[1] = points[index_found]; - if (points.size() == 0) return to_return; - - double distance_x_1 = DBL_MAX; - double distance_1 = DBL_MAX; - double distance_x_2 = DBL_MAX; - double distance_2 = DBL_MAX; - int index_found = 0; - - for (int i = 0; i < points.size(); i++) { - double distance_x = findPointsDistanceOnX(pivot, points[i]); - double distance = findPointsDistance(pivot, points[i]); - - if (distance_x < distance_x_1 && distance_x != 0 && distance <= distance_1) { - distance_x_1 = distance_x; - distance_1 = distance; - index_found = i; - } } - to_return[0] = points[index_found]; + double FingerCount::findPointsDistanceOnX(Point a, Point b) { + double to_return = 0.0; - for (int i = 0; i < points.size(); i++) { - double distance_x = findPointsDistanceOnX(pivot, points[i]); - double distance = findPointsDistance(pivot, points[i]); + if (a.x > b.x) + to_return = a.x - b.x; + else + to_return = b.x - a.x; - if (distance_x < distance_x_2 && distance_x != 0 && distance <= distance_2 && distance_x != distance_x_1) { - distance_x_2 = distance_x; - distance_2 = distance; - index_found = i; - } + return to_return; } - to_return[1] = points[index_found]; - - return to_return; -} - -double FingerCount::findPointsDistanceOnX(Point a, Point b) { - double to_return = 0.0; - - if (a.x > b.x) - to_return = a.x - b.x; - else - to_return = b.x - a.x; - - return to_return; -} - -void FingerCount::drawVectorPoints(Mat image, vector points, Scalar color, bool with_numbers) { - for (int i = 0; i < points.size(); i++) { - circle(image, points[i], 5, color, 2, 8); - if (with_numbers) - putText(image, to_string(i), points[i], FONT_HERSHEY_PLAIN, 3, color); + void FingerCount::drawVectorPoints(Mat image, vector points, Scalar color, bool with_numbers) { + for (int i = 0; i < points.size(); i++) { + circle(image, points[i], 5, color, 2, 8); + if (with_numbers) + putText(image, to_string(i), points[i], FONT_HERSHEY_PLAIN, 3, color); + } } } \ No newline at end of file diff --git a/src/computervision/FingerCount.h b/src/computervision/FingerCount.h index 3f0020b..357bc5f 100644 --- a/src/computervision/FingerCount.h +++ b/src/computervision/FingerCount.h @@ -1,32 +1,36 @@ #pragma once -#include "opencv/cv.h" +#include "opencv2/core.hpp" +#include /* Author: Nicoḷ Castellazzi https://github.com/nicast */ -using namespace cv; -using namespace std; +namespace computervision +{ + using namespace cv; + using namespace std; -class FingerCount { -public: - FingerCount(void); - Mat findFingersCount(Mat input_image, Mat frame); + class FingerCount { + public: + FingerCount(void); + Mat findFingersCount(Mat input_image, Mat frame); -private: - Scalar color_blue; - Scalar color_green; - Scalar color_red; - Scalar color_black; - Scalar color_white; - Scalar color_yellow; - Scalar color_purple; - double findPointsDistance(Point a, Point b); - vector compactOnNeighborhoodMedian(vector points, double max_neighbor_distance); - double findAngle(Point a, Point b, Point c); - bool isFinger(Point a, Point b, Point c, double limit_angle_inf, double limit_angle_sup, cv::Point palm_center, double distance_from_palm_tollerance); - vector findClosestOnX(vector points, Point pivot); - double findPointsDistanceOnX(Point a, Point b); - void drawVectorPoints(Mat image, vector points, Scalar color, bool with_numbers); -}; \ No newline at end of file + private: + Scalar color_blue; + Scalar color_green; + Scalar color_red; + Scalar color_black; + Scalar color_white; + Scalar color_yellow; + Scalar color_purple; + double findPointsDistance(Point a, Point b); + vector compactOnNeighborhoodMedian(vector points, double max_neighbor_distance); + double findAngle(Point a, Point b, Point c); + bool isFinger(Point a, Point b, Point c, double limit_angle_inf, double limit_angle_sup, cv::Point palm_center, double distance_from_palm_tollerance); + vector findClosestOnX(vector points, Point pivot); + double findPointsDistanceOnX(Point a, Point b); + void drawVectorPoints(Mat image, vector points, Scalar color, bool with_numbers); + }; +} \ No newline at end of file diff --git a/src/computervision/ObjectDetection.cpp b/src/computervision/ObjectDetection.cpp index a3c5568..1942c18 100644 --- a/src/computervision/ObjectDetection.cpp +++ b/src/computervision/ObjectDetection.cpp @@ -1,6 +1,13 @@ +#include "opencv2/opencv.hpp" +#include "opencv2/imgcodecs.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/videoio.hpp" +#include +#include + #include "ObjectDetection.h" #include "ObjectDetection.h" -//#include "BackgroundRemover.h" +#include "BackgroundRemover.h" #include "SkinDetector.h" #include "FaceDetector.h" #include "FingerCount.h" @@ -8,10 +15,11 @@ namespace computervision { cv::VideoCapture cap(0); + cv::Mat img, imgGray, img2, img2Gray, img3, img4; Mat frame, frameOut, handMask, foreground, fingerCountDebug; - //BackgroundRemover backgroundRemover; + BackgroundRemover backgroundRemover; SkinDetector skinDetector; FaceDetector faceDetector; FingerCount fingerCount; @@ -21,15 +29,24 @@ namespace computervision { } - void ObjectDetection::Init() + bool ObjectDetection::Init() { + if (!cap.isOpened()) { + cout << "Can't find camera!" << endl; + return false; + } + cap.read(frame); + frameOut = frame.clone(); + skinDetector.drawSkinColorSampler(frameOut); foreground = backgroundRemover.getForeground(frame); faceDetector.removeFaces(frame, foreground); handMask = skinDetector.getSkinMask(foreground); + + return true; } void ObjectDetection::readWebcam() diff --git a/src/computervision/ObjectDetection.h b/src/computervision/ObjectDetection.h index dca7180..3e75f73 100644 --- a/src/computervision/ObjectDetection.h +++ b/src/computervision/ObjectDetection.h @@ -17,6 +17,7 @@ namespace computervision public: ObjectDetection(); + bool Init(); void readWebcam(); void showWebcam(); void calculateDifference(); diff --git a/src/computervision/SkinDetector.cpp b/src/computervision/SkinDetector.cpp index 88ad013..595930e 100644 --- a/src/computervision/SkinDetector.cpp +++ b/src/computervision/SkinDetector.cpp @@ -1,103 +1,105 @@ #include "SkinDetector.h" -#include"opencv2\opencv.hpp" /* Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti */ -SkinDetector::SkinDetector(void) { - hLowThreshold = 0; - hHighThreshold = 0; - sLowThreshold = 0; - sHighThreshold = 0; - vLowThreshold = 0; - vHighThreshold = 0; +namespace computervision +{ + SkinDetector::SkinDetector(void) { + hLowThreshold = 0; + hHighThreshold = 0; + sLowThreshold = 0; + sHighThreshold = 0; + vLowThreshold = 0; + vHighThreshold = 0; - calibrated = false; + calibrated = false; - skinColorSamplerRectangle1, skinColorSamplerRectangle2; -} + skinColorSamplerRectangle1, skinColorSamplerRectangle2; + } -void SkinDetector::drawSkinColorSampler(Mat input) { - int frameWidth = input.size().width, frameHeight = input.size().height; + void SkinDetector::drawSkinColorSampler(Mat input) { + int frameWidth = input.size().width, frameHeight = input.size().height; - int rectangleSize = 20; - Scalar rectangleColor = Scalar(255, 0, 255); + int rectangleSize = 20; + Scalar rectangleColor = Scalar(255, 0, 255); - skinColorSamplerRectangle1 = Rect(frameWidth / 5, frameHeight / 2, rectangleSize, rectangleSize); - skinColorSamplerRectangle2 = Rect(frameWidth / 5, frameHeight / 3, rectangleSize, rectangleSize); + skinColorSamplerRectangle1 = Rect(frameWidth / 5, frameHeight / 2, rectangleSize, rectangleSize); + skinColorSamplerRectangle2 = Rect(frameWidth / 5, frameHeight / 3, rectangleSize, rectangleSize); - rectangle( - input, - skinColorSamplerRectangle1, - rectangleColor - ); + rectangle( + input, + skinColorSamplerRectangle1, + rectangleColor + ); - rectangle( - input, - skinColorSamplerRectangle2, - rectangleColor - ); -} + rectangle( + input, + skinColorSamplerRectangle2, + rectangleColor + ); + } -void SkinDetector::calibrate(Mat input) { + void SkinDetector::calibrate(Mat input) { - Mat hsvInput; - cvtColor(input, hsvInput, CV_BGR2HSV); + Mat hsvInput; + cvtColor(input, hsvInput, CV_BGR2HSV); - Mat sample1 = Mat(hsvInput, skinColorSamplerRectangle1); - Mat sample2 = Mat(hsvInput, skinColorSamplerRectangle2); + Mat sample1 = Mat(hsvInput, skinColorSamplerRectangle1); + Mat sample2 = Mat(hsvInput, skinColorSamplerRectangle2); - calculateThresholds(sample1, sample2); + calculateThresholds(sample1, sample2); - calibrated = true; -} + calibrated = true; + } -void SkinDetector::calculateThresholds(Mat sample1, Mat sample2) { - int offsetLowThreshold = 80; - int offsetHighThreshold = 30; + void SkinDetector::calculateThresholds(Mat sample1, Mat sample2) { + int offsetLowThreshold = 80; + int offsetHighThreshold = 30; - Scalar hsvMeansSample1 = mean(sample1); - Scalar hsvMeansSample2 = mean(sample2); + Scalar hsvMeansSample1 = mean(sample1); + Scalar hsvMeansSample2 = mean(sample2); - hLowThreshold = min(hsvMeansSample1[0], hsvMeansSample2[0]) - offsetLowThreshold; - hHighThreshold = max(hsvMeansSample1[0], hsvMeansSample2[0]) + offsetHighThreshold; + hLowThreshold = min(hsvMeansSample1[0], hsvMeansSample2[0]) - offsetLowThreshold; + hHighThreshold = max(hsvMeansSample1[0], hsvMeansSample2[0]) + offsetHighThreshold; - sLowThreshold = min(hsvMeansSample1[1], hsvMeansSample2[1]) - offsetLowThreshold; - sHighThreshold = max(hsvMeansSample1[1], hsvMeansSample2[1]) + offsetHighThreshold; + sLowThreshold = min(hsvMeansSample1[1], hsvMeansSample2[1]) - offsetLowThreshold; + sHighThreshold = max(hsvMeansSample1[1], hsvMeansSample2[1]) + offsetHighThreshold; - // the V channel shouldn't be used. By ignorint it, shadows on the hand wouldn't interfire with segmentation. - // Unfortunately there's a bug somewhere and not using the V channel causes some problem. This shouldn't be too hard to fix. - vLowThreshold = min(hsvMeansSample1[2], hsvMeansSample2[2]) - offsetLowThreshold; - vHighThreshold = max(hsvMeansSample1[2], hsvMeansSample2[2]) + offsetHighThreshold; - //vLowThreshold = 0; - //vHighThreshold = 255; -} + // the V channel shouldn't be used. By ignorint it, shadows on the hand wouldn't interfire with segmentation. + // Unfortunately there's a bug somewhere and not using the V channel causes some problem. This shouldn't be too hard to fix. + vLowThreshold = min(hsvMeansSample1[2], hsvMeansSample2[2]) - offsetLowThreshold; + vHighThreshold = max(hsvMeansSample1[2], hsvMeansSample2[2]) + offsetHighThreshold; + //vLowThreshold = 0; + //vHighThreshold = 255; + } -Mat SkinDetector::getSkinMask(Mat input) { - Mat skinMask; + Mat SkinDetector::getSkinMask(Mat input) { + Mat skinMask; + + if (!calibrated) { + skinMask = Mat::zeros(input.size(), CV_8UC1); + return skinMask; + } + + Mat hsvInput; + cvtColor(input, hsvInput, CV_BGR2HSV); + + inRange( + hsvInput, + Scalar(hLowThreshold, sLowThreshold, vLowThreshold), + Scalar(hHighThreshold, sHighThreshold, vHighThreshold), + skinMask); + + performOpening(skinMask, MORPH_ELLIPSE, { 3, 3 }); + dilate(skinMask, skinMask, Mat(), Point(-1, -1), 3); - if (!calibrated) { - skinMask = Mat::zeros(input.size(), CV_8UC1); return skinMask; } - Mat hsvInput; - cvtColor(input, hsvInput, CV_BGR2HSV); - - inRange( - hsvInput, - Scalar(hLowThreshold, sLowThreshold, vLowThreshold), - Scalar(hHighThreshold, sHighThreshold, vHighThreshold), - skinMask); - - performOpening(skinMask, MORPH_ELLIPSE, { 3, 3 }); - dilate(skinMask, skinMask, Mat(), Point(-1, -1), 3); - - return skinMask; -} - -void SkinDetector::performOpening(Mat binaryImage, int kernelShape, Point kernelSize) { - Mat structuringElement = getStructuringElement(kernelShape, kernelSize); - morphologyEx(binaryImage, binaryImage, MORPH_OPEN, structuringElement); + void SkinDetector::performOpening(Mat binaryImage, int kernelShape, Point kernelSize) { + Mat structuringElement = getStructuringElement(kernelShape, kernelSize); + morphologyEx(binaryImage, binaryImage, MORPH_OPEN, structuringElement); + } } \ No newline at end of file diff --git a/src/computervision/SkinDetector.h b/src/computervision/SkinDetector.h index 1c71aee..d4e0cac 100644 --- a/src/computervision/SkinDetector.h +++ b/src/computervision/SkinDetector.h @@ -1,34 +1,39 @@ #pragma once -#include - +#include +#include +#include +#include /* Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti */ -using namespace cv; -using namespace std; +namespace computervision +{ + using namespace cv; + using namespace std; -class SkinDetector { -public: - SkinDetector(void); + class SkinDetector { + public: + SkinDetector(void); - void drawSkinColorSampler(Mat input); - void calibrate(Mat input); - Mat getSkinMask(Mat input); + void drawSkinColorSampler(Mat input); + void calibrate(Mat input); + Mat getSkinMask(Mat input); -private: - int hLowThreshold = 0; - int hHighThreshold = 0; - int sLowThreshold = 0; - int sHighThreshold = 0; - int vLowThreshold = 0; - int vHighThreshold = 0; + private: + int hLowThreshold = 0; + int hHighThreshold = 0; + int sLowThreshold = 0; + int sHighThreshold = 0; + int vLowThreshold = 0; + int vHighThreshold = 0; - bool calibrated = false; + bool calibrated = false; - Rect skinColorSamplerRectangle1, skinColorSamplerRectangle2; + Rect skinColorSamplerRectangle1, skinColorSamplerRectangle2; - void calculateThresholds(Mat sample1, Mat sample2); - void SkinDetector::performOpening(Mat binaryImage, int structuralElementShapde, Point structuralElementSize); -}; \ No newline at end of file + void calculateThresholds(Mat sample1, Mat sample2); + void performOpening(Mat binaryImage, int structuralElementShapde, Point structuralElementSize); + }; +} \ No newline at end of file diff --git a/wk2_fps.vcxproj b/wk2_fps.vcxproj index eb6a38b..4c4fbdf 100644 --- a/wk2_fps.vcxproj +++ b/wk2_fps.vcxproj @@ -37,7 +37,7 @@ - + diff --git a/wk2_fps.vcxproj.filters b/wk2_fps.vcxproj.filters index 4fdf700..260ce09 100644 --- a/wk2_fps.vcxproj.filters +++ b/wk2_fps.vcxproj.filters @@ -101,7 +101,7 @@ Header Files - + Header Files