[EDIT] added evrything to namespace, also fixed includes

This commit is contained in:
Jasper
2021-05-21 12:12:42 +02:00
parent e39cb1a761
commit 27a09aeca4
12 changed files with 493 additions and 454 deletions

View File

@@ -1,58 +1,59 @@
#include "BackgroundRemover.h" #include "BackgroundRemover.h"
#include"opencv2\opencv.hpp"
/* /*
Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti
*/ */
namespace computervision
{
BackgroundRemover::BackgroundRemover(void) {
background;
calibrated = false;
}
BackgroundRemover::BackgroundRemover(void) { void BackgroundRemover::calibrate(Mat input) {
background; cvtColor(input, background, CV_BGR2GRAY);
calibrated = false; calibrated = true;
} }
void BackgroundRemover::calibrate(Mat input) { Mat BackgroundRemover::getForeground(Mat input) {
cvtColor(input, background, CV_BGR2GRAY); Mat foregroundMask = getForegroundMask(input);
calibrated = true;
}
Mat BackgroundRemover::getForeground(Mat input) { //imshow("foregroundMask", foregroundMask);
Mat foregroundMask = getForegroundMask(input);
//imshow("foregroundMask", foregroundMask); Mat foreground;
input.copyTo(foreground, foregroundMask);
Mat foreground; return foreground;
input.copyTo(foreground, foregroundMask); }
return foreground; Mat BackgroundRemover::getForegroundMask(Mat input) {
} Mat foregroundMask;
Mat BackgroundRemover::getForegroundMask(Mat input) { if (!calibrated) {
Mat foregroundMask; 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; 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<uchar>(i, j);
uchar bgPixel = background.at<uchar>(i, j);
return foregroundMask; if (framePixel >= bgPixel - thresholdOffset && framePixel <= bgPixel + thresholdOffset)
} input.at<uchar>(i, j) = 0;
else
void BackgroundRemover::removeBackground(Mat input, Mat background) { input.at<uchar>(i, j) = 255;
int thresholdOffset = 10; }
for (int i = 0; i < input.rows; i++) {
for (int j = 0; j < input.cols; j++) {
uchar framePixel = input.at<uchar>(i, j);
uchar bgPixel = background.at<uchar>(i, j);
if (framePixel >= bgPixel - thresholdOffset && framePixel <= bgPixel + thresholdOffset)
input.at<uchar>(i, j) = 0;
else
input.at<uchar>(i, j) = 255;
} }
} }
} }

View File

@@ -1,24 +1,25 @@
#pragma once #pragma once
#include"opencv2\opencv.hpp"
#include<opencv\cv.h> #include <opencv2/imgproc\types_c.h>
/* /*
Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti
*/ */
namespace computervision
{
using namespace cv; using namespace cv;
using namespace std; using namespace std;
class BackgroundRemover { class BackgroundRemover {
public: public:
BackgroundRemover(void); BackgroundRemover(void);
void calibrate(Mat input); void calibrate(Mat input);
Mat BackgroundRemover::getForeground(Mat input); Mat getForeground(Mat input);
private: private:
Mat background; Mat background;
bool calibrated = false; bool calibrated = false;
Mat getForegroundMask(Mat input); Mat getForegroundMask(Mat input);
void BackgroundRemover::removeBackground(Mat input, Mat background); void removeBackground(Mat input, Mat background);
}; };
}

View File

@@ -1,51 +1,53 @@
#include "FaceDetector.h" #include "FaceDetector.h"
#include"opencv2\opencv.hpp"
/* /*
Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti 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"; FaceDetector::FaceDetector(void) {
CascadeClassifier faceCascadeClassifier; if (!faceCascadeClassifier.load(faceClassifierFileName))
throw runtime_error("can't load file " + faceClassifierFileName);
FaceDetector::FaceDetector(void) {
if (!faceCascadeClassifier.load(faceClassifierFileName))
throw runtime_error("can't load file " + faceClassifierFileName);
}
void FaceDetector::removeFaces(Mat input, Mat output) {
vector<Rect> 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
);
} }
}
Rect getFaceRect(Mat input) { void FaceDetector::removeFaces(Mat input, Mat output) {
vector<Rect> faceRectangles; vector<Rect> faces;
Mat inputGray; Mat frameGray;
cvtColor(input, inputGray, CV_BGR2GRAY); cvtColor(input, frameGray, CV_BGR2GRAY);
equalizeHist(inputGray, inputGray); 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) for (size_t i = 0; i < faces.size(); i++) {
return faceRectangles[0]; rectangle(
else output,
return Rect(0, 0, 1, 1); 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<Rect> 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);
}
} }

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include"opencv2\opencv.hpp"
#include<opencv\cv.h> #include <opencv2\imgproc\types_c.h>
#include <opencv2/objdetect/objdetect_c.h>
/* /*
Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti
*/ */
@@ -9,8 +9,11 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
class FaceDetector { namespace computervision
public: {
FaceDetector(void); class FaceDetector {
void removeFaces(Mat input, Mat output); public:
}; FaceDetector(void);
void removeFaces(Mat input, Mat output);
};
}

View File

@@ -12,277 +12,280 @@
#define BOUNDING_RECT_FINGER_SIZE_SCALING 0.3 #define BOUNDING_RECT_FINGER_SIZE_SCALING 0.3
#define BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING 0.05 #define BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING 0.05
FingerCount::FingerCount(void) { namespace computervision
color_blue = Scalar(255, 0, 0); {
color_green = Scalar(0, 255, 0); FingerCount::FingerCount(void) {
color_red = Scalar(0, 0, 255); color_blue = Scalar(255, 0, 0);
color_black = Scalar(0, 0, 0); color_green = Scalar(0, 255, 0);
color_white = Scalar(255, 255, 255); color_red = Scalar(0, 0, 255);
color_yellow = Scalar(0, 255, 255); color_black = Scalar(0, 0, 0);
color_purple = Scalar(255, 0, 255); 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<vector<Point>> contours;
vector<Vec4i> 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;
}
} }
if (biggest_contour_index < 0) Mat FingerCount::findFingersCount(Mat input_image, Mat frame) {
return contours_image; 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 // check if the source image is good
vector<Point> hull_points; if (input_image.empty())
vector<int> hull_ints; return contours_image;
// for drawing the convex hull and for finding the bounding rectangle // 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
convexHull(Mat(contours[biggest_contour_index]), hull_points, true); if (input_image.channels() != 1)
return contours_image;
// for finding the defects vector<vector<Point>> contours;
convexHull(Mat(contours[biggest_contour_index]), hull_ints, false); vector<Vec4i> hierarchy;
// we need at least 3 points to find the defects findContours(input_image, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
vector<Vec4i> defects;
if (hull_ints.size() > 3)
convexityDefects(Mat(contours[biggest_contour_index]), hull_ints, defects);
else
return contours_image;
// we bound the convex hull // we need at least one contour to work
Rect bounding_rectangle = boundingRect(Mat(hull_points)); 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 // find the biggest contour (let's suppose it's our hand)
Point center_bounding_rect( int biggest_contour_index = -1;
(bounding_rectangle.tl().x + bounding_rectangle.br().x) / 2, double biggest_area = 0.0;
(bounding_rectangle.tl().y + bounding_rectangle.br().y) / 2
);
// we separate the defects keeping only the ones of intrest for (int i = 0; i < contours.size(); i++) {
vector<Point> start_points; double area = contourArea(contours[i], false);
vector<Point> far_points; if (area > biggest_area) {
biggest_area = area;
for (int i = 0; i < defects.size(); i++) { biggest_contour_index = 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<Point> filtered_start_points = compactOnNeighborhoodMedian(start_points, bounding_rectangle.height * BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING);
vector<Point> filtered_far_points = compactOnNeighborhoodMedian(far_points, bounding_rectangle.height * BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING);
// now we try to find the fingers
vector<Point> filtered_finger_points;
if (filtered_far_points.size() > 1) {
vector<Point> finger_points;
for (int i = 0; i < filtered_start_points.size(); i++) {
vector<Point> 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() > 0) { if (biggest_contour_index < 0)
return contours_image;
// we have at most five fingers usually :) // find the convex hull object for each contour and the defects, two different data structure are needed by the OpenCV api
while (finger_points.size() > 5) vector<Point> hull_points;
finger_points.pop_back(); vector<int> hull_ints;
// filter out the points too close to each other // for drawing the convex hull and for finding the bounding rectangle
for (int i = 0; i < finger_points.size() - 1; i++) { convexHull(Mat(contours[biggest_contour_index]), hull_points, true);
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 finding the defects
convexHull(Mat(contours[biggest_contour_index]), hull_ints, false);
// we need at least 3 points to find the defects
vector<Vec4i> 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<Point> start_points;
vector<Point> 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<Point> filtered_start_points = compactOnNeighborhoodMedian(start_points, bounding_rectangle.height * BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING);
vector<Point> filtered_far_points = compactOnNeighborhoodMedian(far_points, bounding_rectangle.height * BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING);
// now we try to find the fingers
vector<Point> filtered_finger_points;
if (filtered_far_points.size() > 1) {
vector<Point> finger_points;
for (int i = 0; i < filtered_start_points.size(); i++) {
vector<Point> 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 (finger_points.size() > 0) {
if (findPointsDistanceOnX(finger_points[0], finger_points[finger_points.size() - 1]) > bounding_rectangle.height * BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING * 1.5)
// 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]); 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<Point> FingerCount::compactOnNeighborhoodMedian(vector<Point> points, double max_neighbor_distance) {
vector<Point> 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 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 double FingerCount::findAngle(Point a, Point b, Point c) {
drawContours(contours_image, contours, biggest_contour_index, color_green, 2, 8, hierarchy); double ab = findPointsDistance(a, b);
polylines(contours_image, hull_points, true, color_blue); double bc = findPointsDistance(b, c);
rectangle(contours_image, bounding_rectangle.tl(), bounding_rectangle.br(), color_red, 2, 8, 0); double ac = findPointsDistance(a, c);
circle(contours_image, center_bounding_rect, 5, color_purple, 2, 8); return acos((ab * ab + bc * bc - ac * ac) / (2 * ab * bc)) * 180 / CV_PI;
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<Point> FingerCount::compactOnNeighborhoodMedian(vector<Point> points, double max_neighbor_distance) {
vector<Point> 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;
} }
// last 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) {
median_points.push_back(median); 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) { // the two far points should not be both under the center of the hand
double ab = findPointsDistance(a, b); int delta_y_3 = palm_center.y - a.y;
double bc = findPointsDistance(b, c); int delta_y_4 = palm_center.y - c.y;
double ac = findPointsDistance(a, c); if (delta_y_3 < 0 && delta_y_4 < 0)
return acos((ab * ab + bc * bc - ac * ac) / (2 * ab * bc)) * 180 / CV_PI; 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 distance_from_palm = findPointsDistance(b, palm_center);
double angle = findAngle(a, b, c); if (distance_from_palm < min_distance_from_palm)
if (angle > limit_angle_sup || angle < limit_angle_inf) return false;
return false;
// the finger point sohould not be under the two far points // this should be the case when no fingers are up
int delta_y_1 = b.y - a.y; double distance_from_palm_far_1 = findPointsDistance(a, palm_center);
int delta_y_2 = b.y - c.y; double distance_from_palm_far_2 = findPointsDistance(c, palm_center);
if (delta_y_1 > 0 && delta_y_2 > 0) if (distance_from_palm_far_1 < min_distance_from_palm / 4 || distance_from_palm_far_2 < min_distance_from_palm / 4)
return false; return false;
// the two far points should not be both under the center of the hand return true;
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;
double distance_from_palm = findPointsDistance(b, palm_center); vector<Point> FingerCount::findClosestOnX(vector<Point> points, Point pivot) {
if (distance_from_palm < min_distance_from_palm) vector<Point> to_return(2);
return false;
// this should be the case when no fingers are up if (points.size() == 0)
double distance_from_palm_far_1 = findPointsDistance(a, palm_center); return to_return;
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;
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<Point> FingerCount::findClosestOnX(vector<Point> points, Point pivot) { for (int i = 0; i < points.size(); i++) {
vector<Point> to_return(2); 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; 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++) { if (a.x > b.x)
double distance_x = findPointsDistanceOnX(pivot, points[i]); to_return = a.x - b.x;
double distance = findPointsDistance(pivot, points[i]); else
to_return = b.x - a.x;
if (distance_x < distance_x_2 && distance_x != 0 && distance <= distance_2 && distance_x != distance_x_1) { return to_return;
distance_x_2 = distance_x;
distance_2 = distance;
index_found = i;
}
} }
to_return[1] = points[index_found]; void FingerCount::drawVectorPoints(Mat image, vector<Point> points, Scalar color, bool with_numbers) {
for (int i = 0; i < points.size(); i++) {
return to_return; circle(image, points[i], 5, color, 2, 8);
} if (with_numbers)
putText(image, to_string(i), points[i], FONT_HERSHEY_PLAIN, 3, color);
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<Point> 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);
} }
} }

View File

@@ -1,32 +1,36 @@
#pragma once #pragma once
#include "opencv/cv.h" #include "opencv2/core.hpp"
#include <opencv2/imgproc/types_c.h>
/* /*
Author: Nicol<6F> Castellazzi https://github.com/nicast Author: Nicol<6F> Castellazzi https://github.com/nicast
*/ */
using namespace cv; namespace computervision
using namespace std; {
using namespace cv;
using namespace std;
class FingerCount { class FingerCount {
public: public:
FingerCount(void); FingerCount(void);
Mat findFingersCount(Mat input_image, Mat frame); Mat findFingersCount(Mat input_image, Mat frame);
private: private:
Scalar color_blue; Scalar color_blue;
Scalar color_green; Scalar color_green;
Scalar color_red; Scalar color_red;
Scalar color_black; Scalar color_black;
Scalar color_white; Scalar color_white;
Scalar color_yellow; Scalar color_yellow;
Scalar color_purple; Scalar color_purple;
double findPointsDistance(Point a, Point b); double findPointsDistance(Point a, Point b);
vector<Point> compactOnNeighborhoodMedian(vector<Point> points, double max_neighbor_distance); vector<Point> compactOnNeighborhoodMedian(vector<Point> points, double max_neighbor_distance);
double findAngle(Point a, Point b, Point c); 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); 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<Point> findClosestOnX(vector<Point> points, Point pivot); vector<Point> findClosestOnX(vector<Point> points, Point pivot);
double findPointsDistanceOnX(Point a, Point b); double findPointsDistanceOnX(Point a, Point b);
void drawVectorPoints(Mat image, vector<Point> points, Scalar color, bool with_numbers); void drawVectorPoints(Mat image, vector<Point> points, Scalar color, bool with_numbers);
}; };
}

View File

@@ -1,6 +1,13 @@
#include "opencv2/opencv.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/videoio.hpp"
#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>
#include "ObjectDetection.h" #include "ObjectDetection.h"
#include "ObjectDetection.h" #include "ObjectDetection.h"
//#include "BackgroundRemover.h" #include "BackgroundRemover.h"
#include "SkinDetector.h" #include "SkinDetector.h"
#include "FaceDetector.h" #include "FaceDetector.h"
#include "FingerCount.h" #include "FingerCount.h"
@@ -8,10 +15,11 @@
namespace computervision namespace computervision
{ {
cv::VideoCapture cap(0); cv::VideoCapture cap(0);
cv::Mat img, imgGray, img2, img2Gray, img3, img4; cv::Mat img, imgGray, img2, img2Gray, img3, img4;
Mat frame, frameOut, handMask, foreground, fingerCountDebug; Mat frame, frameOut, handMask, foreground, fingerCountDebug;
//BackgroundRemover backgroundRemover; BackgroundRemover backgroundRemover;
SkinDetector skinDetector; SkinDetector skinDetector;
FaceDetector faceDetector; FaceDetector faceDetector;
FingerCount fingerCount; 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); cap.read(frame);
frameOut = frame.clone();
skinDetector.drawSkinColorSampler(frameOut); skinDetector.drawSkinColorSampler(frameOut);
foreground = backgroundRemover.getForeground(frame); foreground = backgroundRemover.getForeground(frame);
faceDetector.removeFaces(frame, foreground); faceDetector.removeFaces(frame, foreground);
handMask = skinDetector.getSkinMask(foreground); handMask = skinDetector.getSkinMask(foreground);
return true;
} }
void ObjectDetection::readWebcam() void ObjectDetection::readWebcam()

View File

@@ -17,6 +17,7 @@ namespace computervision
public: public:
ObjectDetection(); ObjectDetection();
bool Init();
void readWebcam(); void readWebcam();
void showWebcam(); void showWebcam();
void calculateDifference(); void calculateDifference();

View File

@@ -1,103 +1,105 @@
#include "SkinDetector.h" #include "SkinDetector.h"
#include"opencv2\opencv.hpp"
/* /*
Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti
*/ */
SkinDetector::SkinDetector(void) { namespace computervision
hLowThreshold = 0; {
hHighThreshold = 0; SkinDetector::SkinDetector(void) {
sLowThreshold = 0; hLowThreshold = 0;
sHighThreshold = 0; hHighThreshold = 0;
vLowThreshold = 0; sLowThreshold = 0;
vHighThreshold = 0; sHighThreshold = 0;
vLowThreshold = 0;
vHighThreshold = 0;
calibrated = false; calibrated = false;
skinColorSamplerRectangle1, skinColorSamplerRectangle2; skinColorSamplerRectangle1, skinColorSamplerRectangle2;
} }
void SkinDetector::drawSkinColorSampler(Mat input) { void SkinDetector::drawSkinColorSampler(Mat input) {
int frameWidth = input.size().width, frameHeight = input.size().height; int frameWidth = input.size().width, frameHeight = input.size().height;
int rectangleSize = 20; int rectangleSize = 20;
Scalar rectangleColor = Scalar(255, 0, 255); Scalar rectangleColor = Scalar(255, 0, 255);
skinColorSamplerRectangle1 = Rect(frameWidth / 5, frameHeight / 2, rectangleSize, rectangleSize); skinColorSamplerRectangle1 = Rect(frameWidth / 5, frameHeight / 2, rectangleSize, rectangleSize);
skinColorSamplerRectangle2 = Rect(frameWidth / 5, frameHeight / 3, rectangleSize, rectangleSize); skinColorSamplerRectangle2 = Rect(frameWidth / 5, frameHeight / 3, rectangleSize, rectangleSize);
rectangle( rectangle(
input, input,
skinColorSamplerRectangle1, skinColorSamplerRectangle1,
rectangleColor rectangleColor
); );
rectangle( rectangle(
input, input,
skinColorSamplerRectangle2, skinColorSamplerRectangle2,
rectangleColor rectangleColor
); );
} }
void SkinDetector::calibrate(Mat input) { void SkinDetector::calibrate(Mat input) {
Mat hsvInput; Mat hsvInput;
cvtColor(input, hsvInput, CV_BGR2HSV); cvtColor(input, hsvInput, CV_BGR2HSV);
Mat sample1 = Mat(hsvInput, skinColorSamplerRectangle1); Mat sample1 = Mat(hsvInput, skinColorSamplerRectangle1);
Mat sample2 = Mat(hsvInput, skinColorSamplerRectangle2); Mat sample2 = Mat(hsvInput, skinColorSamplerRectangle2);
calculateThresholds(sample1, sample2); calculateThresholds(sample1, sample2);
calibrated = true; calibrated = true;
} }
void SkinDetector::calculateThresholds(Mat sample1, Mat sample2) { void SkinDetector::calculateThresholds(Mat sample1, Mat sample2) {
int offsetLowThreshold = 80; int offsetLowThreshold = 80;
int offsetHighThreshold = 30; int offsetHighThreshold = 30;
Scalar hsvMeansSample1 = mean(sample1); Scalar hsvMeansSample1 = mean(sample1);
Scalar hsvMeansSample2 = mean(sample2); Scalar hsvMeansSample2 = mean(sample2);
hLowThreshold = min(hsvMeansSample1[0], hsvMeansSample2[0]) - offsetLowThreshold; hLowThreshold = min(hsvMeansSample1[0], hsvMeansSample2[0]) - offsetLowThreshold;
hHighThreshold = max(hsvMeansSample1[0], hsvMeansSample2[0]) + offsetHighThreshold; hHighThreshold = max(hsvMeansSample1[0], hsvMeansSample2[0]) + offsetHighThreshold;
sLowThreshold = min(hsvMeansSample1[1], hsvMeansSample2[1]) - offsetLowThreshold; sLowThreshold = min(hsvMeansSample1[1], hsvMeansSample2[1]) - offsetLowThreshold;
sHighThreshold = max(hsvMeansSample1[1], hsvMeansSample2[1]) + offsetHighThreshold; 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. // 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. // 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; vLowThreshold = min(hsvMeansSample1[2], hsvMeansSample2[2]) - offsetLowThreshold;
vHighThreshold = max(hsvMeansSample1[2], hsvMeansSample2[2]) + offsetHighThreshold; vHighThreshold = max(hsvMeansSample1[2], hsvMeansSample2[2]) + offsetHighThreshold;
//vLowThreshold = 0; //vLowThreshold = 0;
//vHighThreshold = 255; //vHighThreshold = 255;
} }
Mat SkinDetector::getSkinMask(Mat input) { Mat SkinDetector::getSkinMask(Mat input) {
Mat skinMask; 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; return skinMask;
} }
Mat hsvInput; void SkinDetector::performOpening(Mat binaryImage, int kernelShape, Point kernelSize) {
cvtColor(input, hsvInput, CV_BGR2HSV); Mat structuringElement = getStructuringElement(kernelShape, kernelSize);
morphologyEx(binaryImage, binaryImage, MORPH_OPEN, structuringElement);
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);
} }

View File

@@ -1,34 +1,39 @@
#pragma once #pragma once
#include<opencv\cv.h> #include<opencv2\core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
/* /*
Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti
*/ */
using namespace cv; namespace computervision
using namespace std; {
using namespace cv;
using namespace std;
class SkinDetector { class SkinDetector {
public: public:
SkinDetector(void); SkinDetector(void);
void drawSkinColorSampler(Mat input); void drawSkinColorSampler(Mat input);
void calibrate(Mat input); void calibrate(Mat input);
Mat getSkinMask(Mat input); Mat getSkinMask(Mat input);
private: private:
int hLowThreshold = 0; int hLowThreshold = 0;
int hHighThreshold = 0; int hHighThreshold = 0;
int sLowThreshold = 0; int sLowThreshold = 0;
int sHighThreshold = 0; int sHighThreshold = 0;
int vLowThreshold = 0; int vLowThreshold = 0;
int vHighThreshold = 0; int vHighThreshold = 0;
bool calibrated = false; bool calibrated = false;
Rect skinColorSamplerRectangle1, skinColorSamplerRectangle2; Rect skinColorSamplerRectangle1, skinColorSamplerRectangle2;
void calculateThresholds(Mat sample1, Mat sample2); void calculateThresholds(Mat sample1, Mat sample2);
void SkinDetector::performOpening(Mat binaryImage, int structuralElementShapde, Point structuralElementSize); void performOpening(Mat binaryImage, int structuralElementShapde, Point structuralElementSize);
}; };
}

View File

@@ -37,7 +37,7 @@
<ItemGroup> <ItemGroup>
<ClInclude Include="src\computervision\FaceDetector.h" /> <ClInclude Include="src\computervision\FaceDetector.h" />
<ClInclude Include="src\computervision\FingerCount.h" /> <ClInclude Include="src\computervision\FingerCount.h" />
<ClInclude Include="src\computervision\Header.h" /> <ClInclude Include="src\computervision\BackgroundRemover.h" />
<ClInclude Include="src\computervision\SkinDetector.h" /> <ClInclude Include="src\computervision\SkinDetector.h" />
<ClInclude Include="src\computervision\ObjectDetection.h" /> <ClInclude Include="src\computervision\ObjectDetection.h" />
<ClInclude Include="src\entities\camera.h" /> <ClInclude Include="src\entities\camera.h" />

View File

@@ -101,7 +101,7 @@
<ClInclude Include="src\computervision\FaceDetector.h"> <ClInclude Include="src\computervision\FaceDetector.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\computervision\Header.h"> <ClInclude Include="src\computervision\BackgroundRemover.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>