Compare commits

24 Commits

Author SHA1 Message Date
Sem van der Hoeven
612adf6e9b [EDIT] mutex? 2021-05-31 15:50:06 +02:00
Sem van der Hoeven
25c99abb72 [EDIT] mutex with cap 2021-05-31 10:25:24 +02:00
Jasper
9d80cddbd1 [BUG] image still doesnt work 2021-05-28 16:49:13 +02:00
Sem van der Hoeven
40529f84b3 [ADD] basis for async arm detection 2021-05-28 15:31:21 +02:00
Jasper
a68c6a57bf [EDIT] edited file 2021-05-28 12:32:10 +02:00
Jasper
078a6ce66d [ADD] added all the files 2021-05-28 12:27:12 +02:00
Sem van der Hoeven
f1f1aac93d [ADD] comments 2021-05-25 15:54:02 +02:00
Sem van der Hoeven
563f465e2c [EDIT] remove unused methods 2021-05-25 15:46:53 +02:00
Sem van der Hoeven
05ae8ee019 [FEATURE] finished hand open/closed recognition 2021-05-25 14:49:04 +02:00
Sem van der Hoeven
3696e2eb30 [EDIT] improve hand detection with mask 2021-05-25 14:19:18 +02:00
Sem van der Hoeven
276aa1a449 [ADD] mask methods 2021-05-25 13:31:25 +02:00
Sem van der Hoeven
ad4075a826 [EDIT] change window size to ints 2021-05-25 10:19:33 +02:00
Sem van der Hoeven
e50cd92a35 [ADD] some headers 2021-05-25 10:16:58 +02:00
Sem van der Hoeven
ff79c1525c Merge branch 'feature/objectdetection' into develop 2021-05-21 15:25:29 +02:00
Nathalie Seen
a7597c8d4f [ADD] comments to backgroundRemover 2021-05-21 15:24:06 +02:00
Sem van der Hoeven
5b4d9b624f Merge branch 'feature/objectdetection' into develop 2021-05-21 15:22:38 +02:00
Sem van der Hoeven
27aca98ea4 Merge branch 'feature/comments' into feature/objectdetection 2021-05-21 15:22:07 +02:00
Sem van der Hoeven
ca591dd427 [ADD] comments to fingercount 2021-05-21 15:21:03 +02:00
Jasper
8720e50ba8 [ADD] added comments to the classes FaceDetector and ObjectDetection 2021-05-21 15:10:05 +02:00
Sem van der Hoeven
acf24cab36 [ADD] comments to skindetector 2021-05-21 14:56:45 +02:00
Sem van der Hoeven
1811bf51a4 [EDIT] added base for hand detection 2021-05-21 13:23:33 +02:00
Jasper
27a09aeca4 [EDIT] added evrything to namespace, also fixed includes 2021-05-21 12:12:42 +02:00
Jasper
e39cb1a761 [ADD] added handy files 2021-05-21 11:52:47 +02:00
Sem van der Hoeven
ab3b0c296a [ADD] start for object detection 2021-05-21 11:19:17 +02:00
25 changed files with 30825 additions and 50 deletions

2
.gitignore vendored
View File

@@ -428,4 +428,6 @@ FodyWeavers.xsd
**/docs/*
**/doc/*
**/pose_iter_160000.caffemodel
# End of https://www.toptal.com/developers/gitignore/api/c++,visualstudio,visualstudiocode,opencv

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,59 @@
#include "BackgroundRemover.h"
/*
Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti
*/
namespace computervision
{
BackgroundRemover::BackgroundRemover(void) {
background;
calibrated = false;
}
void BackgroundRemover::calibrate(Mat input) {
cvtColor(input, background, CV_BGR2GRAY);
calibrated = true;
}
Mat BackgroundRemover::getForeground(Mat input) {
Mat foregroundMask = getForegroundMask(input);
//imshow("foregroundMask", foregroundMask);
Mat foreground;
input.copyTo(foreground, foregroundMask);
return foreground;
}
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);
return foregroundMask;
}
void BackgroundRemover::removeBackground(Mat input, Mat background) {
int thresholdOffset = 25;
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

@@ -0,0 +1,58 @@
#pragma once
#include"opencv2\opencv.hpp"
#include <opencv2/imgproc\types_c.h>
/*
Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti
*/
namespace computervision
{
using namespace cv;
using namespace std;
class BackgroundRemover {
public:
/**
* @brief constructor,
* create background variable and set calibrated to faslse
*
*/
BackgroundRemover(void);
/**
* @brief sets the input image to a grayscale image
* sets calibrated to true
*
* @param input input the image that has to be calibrated
*/
void calibrate(Mat input);
/**
* @brief Gets the mask of the foregorund of the input image
* and copies it to another image
*
* @param input The image from which the forground needs to be picked
* @return The image on which te foregroundmask is copied
*/
Mat getForeground(Mat input);
private:
Mat background;
bool calibrated = false;
/**
* @brief Sets the image to grayscale and removes the background
*
* @param input The image from which the forground needs to be picked
* @return The mask of the foreground of the image
*/
Mat getForegroundMask(Mat input);
/**
* @brief makes everything on the background black
*
* @param input the image from which the background needs to be removed
* @param background the background of the image
*/
void removeBackground(Mat input, Mat background);
};
}

View File

@@ -0,0 +1,53 @@
#include "FaceDetector.h"
/*
Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti
*/
namespace computervision
{
Rect getFaceRect(Mat input);
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<Rect> faces;
Mat frameGray;
cvtColor(input, frameGray, CV_BGR2GRAY);
equalizeHist(frameGray, frameGray);
faceCascadeClassifier.detectMultiScale(frameGray, faces, 1.1, 2, 0 | 2, Size(120, 120)); // HAAR_SCALE_IMAGE is 2
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<Rect> faceRectangles;
Mat inputGray;
cvtColor(input, inputGray, CV_BGR2GRAY);
equalizeHist(inputGray, inputGray);
faceCascadeClassifier.detectMultiScale(inputGray, faceRectangles, 1.1, 2, 0 | 2, Size(120, 120)); // HAAR_SCALE_IMAGE is 2
if (faceRectangles.size() > 0)
return faceRectangles[0];
else
return Rect(0, 0, 1, 1);
}
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/objdetect.hpp>
#include <opencv2/core.hpp>
#include <opencv2/objdetect/objdetect.hpp>
/*
Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti
*/
using namespace cv;
using namespace std;
namespace computervision
{
class FaceDetector {
public:
/**
* @brief Constructor for the class FaceDetector, loads training data from a file
*
*/
FaceDetector(void);
/**
* @brief Detects faces on an image and blocks them with a black rectangle
*
* @param input Input image
* @param output Output image
*/
void removeFaces(Mat input, Mat output);
};
}

View File

@@ -0,0 +1,298 @@
#include "FingerCount.h"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
/*
Author: Nicol<6F> Castellazzi https://github.com/nicast
*/
#define LIMIT_ANGLE_SUP 60
#define LIMIT_ANGLE_INF 5
#define BOUNDING_RECT_FINGER_SIZE_SCALING 0.3
#define BOUNDING_RECT_NEIGHBOR_DISTANCE_SCALING 0.05
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);
}
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)
return contours_image;
// find the convex hull object for each contour and the defects, two different data structure are needed by the OpenCV api
vector<Point> hull_points;
vector<int> hull_ints;
// 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<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() > 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);
amount_of_fingers = filtered_finger_points.size();
return contours_image;
}
int FingerCount::getAmountOfFingers()
{
return amount_of_fingers;
}
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
median_points.push_back(median);
return median_points;
}
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;
}
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;
// 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;
// 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;
double distance_from_palm = findPointsDistance(b, palm_center);
if (distance_from_palm < min_distance_from_palm)
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;
return true;
}
vector<Point> FingerCount::findClosestOnX(vector<Point> points, Point pivot) {
vector<Point> to_return(2);
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];
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];
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<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

@@ -0,0 +1,119 @@
#pragma once
#include "opencv2/core.hpp"
#include <opencv2/imgproc/types_c.h>
/*
Author: Nicol<6F> Castellazzi https://github.com/nicast
*/
namespace computervision
{
using namespace cv;
using namespace std;
class FingerCount {
public:
FingerCount(void);
/**
* @brief gets the amount of fingers that are held up.
*
* @param input_image the source image to find the fingers on. It should be a mask of a hand
* @param frame the frame to draw the resulting values on (how many fingers are held up etc)
* @return a new image with all the data drawn on it.
*/
Mat findFingersCount(Mat input_image, Mat frame);
/**
* @brief gets the currently held-up finger count.
*
* @return the currently held-up finger count
*/
int getAmountOfFingers();
private:
// colors to use
Scalar color_blue;
Scalar color_green;
Scalar color_red;
Scalar color_black;
Scalar color_white;
Scalar color_yellow;
Scalar color_purple;
int amount_of_fingers;
/**
* @brief finds the distance between 2 points.
*
* @param a the first point
* @param b the second point
* @return a double representing the distance
*/
double findPointsDistance(Point a, Point b);
/**
* @brief compacts the given points on their medians.
* what it does is for each point, it checks if the distance to it's neighbour is greater than the
* max distance. If so, it just adds it to the list that is returned. If not, it calculates the
* median and adds it to the returned list
*
* @param points the points to compact
* @param max_neighbor_distance the maximum distance between points
* @return a vector with the points now compacted.
*/
vector<Point> compactOnNeighborhoodMedian(vector<Point> points, double max_neighbor_distance);
/**
* @brief finds the angle between 3 different points.
*
* @param a the first point
* @param b the second point
* @param c the third point
* @return the angle between the 3 points
*/
double findAngle(Point a, Point b, Point c);
/**
* @brief checks if the given points make up a finger.
*
* @param a the first point to check for
* @param b the second point to check for
* @param c the third point to check for
* @param limit_angle_inf the limit of the angle between 2 fingers
* @param limit_angle_sup the limit of the angle between a finger and a convex point
* @param palm_center the center of the palm
* @param distance_from_palm_tollerance the distance from the palm tolerance
* @return true if the points are a finger, false if not.
*/
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);
/**
* @brief finds the closest point to the given point that is in the given list.
*
* @param points the points to check for
* @param pivot the pivot to check against
* @return a vector containing the point that is closest
*/
vector<Point> findClosestOnX(vector<Point> points, Point pivot);
/**
* @brief finds the distance between the x coords of the points.
*
* @param a the first point
* @param b the second point
* @return the distance between the x values
*/
double findPointsDistanceOnX(Point a, Point b);
/**
* @brief draws the points on the image.
*
* @param image the image to draw on
* @param points the points to draw
* @param color the color to draw them with
* @param with_numbers if the numbers should be drawn with the points
*/
void drawVectorPoints(Mat image, vector<Point> points, Scalar color, bool with_numbers);
};
}

View File

@@ -0,0 +1,133 @@
#include <opencv2/highgui.hpp>
#include "ObjectDetection.h"
#include "BackgroundRemover.h"
#include "SkinDetector.h"
#include "FaceDetector.h"
#include "FingerCount.h"
#include "VideoCapture.h"
using namespace videocapture;
namespace computervision
{
cv::Mat img, imgGray, img2, img2Gray, img3, img4;
int handMaskStartXPos, handMaskStartYPos, handMaskWidth, handMaskHeight;
bool handMaskGenerated = false;
Mat frame, frameOut, handMask, foreground, fingerCountDebug;
BackgroundRemover backgroundRemover;
SkinDetector skinDetector;
FaceDetector faceDetector;
FingerCount fingerCount;
ObjectDetection::ObjectDetection()
{
}
cv::Mat ObjectDetection::readCamera() {
/*videocapture::getMutex()->lock();
videocapture::getCap().read(img);
videocapture::getMutex()->unlock();*/
img = videocapture::readFrame();
return img;
}
bool ObjectDetection::detectHand(Mat cameraFrame)
{
Mat inputFrame = generateHandMaskSquare(cameraFrame);
frameOut = inputFrame.clone();
// detect skin color
skinDetector.drawSkinColorSampler(frameOut);
// remove background from image
foreground = backgroundRemover.getForeground(inputFrame);
// detect the hand contours
handMask = skinDetector.getSkinMask(foreground);
// count the amount of fingers and put the info on the matrix
fingerCountDebug = fingerCount.findFingersCount(handMask, frameOut);
// get the amount of fingers
int fingers_amount = fingerCount.getAmountOfFingers();
// draw the hand rectangle on the camera input, and draw text showing if the hand is open or closed.
drawHandMaskRect(&cameraFrame);
string hand_text = fingers_amount > 0 ? "open" : "closed";
putText(cameraFrame,hand_text, Point(10, 75), FONT_HERSHEY_PLAIN, 2.0, Scalar(255, 0, 255),3);
imshow("camera", cameraFrame);
imshow("output", frameOut);
imshow("foreground", foreground);
imshow("handMask", handMask);
imshow("handDetection", fingerCountDebug);
int key = waitKey(1);
if (key == 98) // b, calibrate the background
backgroundRemover.calibrate(inputFrame);
else if (key == 115) // s, calibrate the skin color
skinDetector.calibrate(inputFrame);
return fingers_amount > 0;
}
void ObjectDetection::calculateDifference()
{
//videocapture::getMutex()->lock();
//videocapture::getCap().read(img);
//videocapture::getCap().read(img2);
//videocapture::getMutex()->unlock()
img = videocapture::readFrame();
img2 = videocapture::readFrame();
cv::cvtColor(img, imgGray, cv::COLOR_RGBA2GRAY);
cv::cvtColor(img2, img2Gray, cv::COLOR_RGBA2GRAY);
cv::absdiff(imgGray, img2Gray, img3);
cv::threshold(img3, img4, 50, 170, cv::THRESH_BINARY);
imshow("threshold", img4);
}
cv::Mat ObjectDetection::generateHandMaskSquare(cv::Mat img)
{
handMaskStartXPos = 20;
handMaskStartYPos = img.rows / 5;
handMaskWidth = img.cols / 3;
handMaskHeight = img.cols / 3;
cv::Mat mask = cv::Mat::zeros(img.size(), img.type());
cv::Mat dstImg = cv::Mat::zeros(img.size(), img.type());
cv::rectangle(mask, Rect(handMaskStartXPos, handMaskStartYPos, handMaskWidth, handMaskHeight), Scalar(255, 255, 255), -1);
img.copyTo(dstImg, mask);
handMaskGenerated = true;
return dstImg;
}
bool ObjectDetection::drawHandMaskRect(cv::Mat* input)
{
if (!handMaskGenerated) return false;
rectangle(*input, Rect(handMaskStartXPos, handMaskStartYPos, handMaskWidth, handMaskHeight), Scalar(255, 255, 255));
return true;
}
void ObjectDetection::showWebcam()
{
imshow("Webcam image", img);
}
}

View File

@@ -0,0 +1,74 @@
#pragma once
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
namespace computervision
{
class ObjectDetection
{
private:
public:
/**
* @brief default constructor of ObjectDetection
*
*/
ObjectDetection();
/**
* @brief Displays an image of the current webcam-footage
*
*/
void showWebcam();
/**
* @brief Calculates the difference between two images
* and outputs an image that only shows the difference
*
*/
void calculateDifference();
/**
* @brief generates the square that will hold the mask in which the hand will be detected.
*
* @param img the current camear frame
* @return a matrix containing the mask
*/
cv::Mat generateHandMaskSquare(cv::Mat img);
/**
* @brief reads the camera and returns it in a matrix.
*
* @return the camera frame in a matrix
*/
cv::Mat readCamera();
/**
* @brief detects a hand based on the given hand mask input frame.
*
* @param inputFrame the input frame from the camera
* @return true if hand is open, false if hand is closed
*/
bool detectHand(cv::Mat cameraFrame);
/**
* @brief draws the hand mask rectangle on the given input matrix.
*
* @param input the input matrix to draw the rectangle on
*/
bool drawHandMaskRect(cv::Mat *input);
cv::VideoCapture getCap();
};
}

View File

@@ -0,0 +1,111 @@
#include "OpenPoseVideo.h"
using namespace std;
using namespace cv;
using namespace cv::dnn;
namespace computervision
{
#define MPI
#ifdef MPI
const int POSE_PAIRS[7][2] =
{
{0,1}, {1,2}, {2,3},
{3,4}, {1,5}, {5,6},
{6,7}
};
string protoFile = "res/pose/mpi/pose_deploy_linevec_faster_4_stages.prototxt";
string weightsFile = "res/pose/mpi/pose_iter_160000.caffemodel";
int nPoints = 8;
#endif
#ifdef COCO
const int POSE_PAIRS[17][2] =
{
{1,2}, {1,5}, {2,3},
{3,4}, {5,6}, {6,7},
{1,8}, {8,9}, {9,10},
{1,11}, {11,12}, {12,13},
{1,0}, {0,14},
{14,16}, {0,15}, {15,17}
};
string protoFile = "pose/coco/pose_deploy_linevec.prototxt";
string weightsFile = "pose/coco/pose_iter_440000.caffemodel";
int nPoints = 18;
#endif
Net net;
int inWidth = 368;
int inHeight = 368;
float thresh = 0.01;
void OpenPoseVideo::setup() {
net = readNetFromCaffe(protoFile, weightsFile);
}
cv::Mat OpenPoseVideo::getBlobFromImage(cv::Mat inputImage)
{
Mat frame;
int frameWidth = inputImage.size().width;
int frameHeight = inputImage.size().height;
double t = (double)cv::getTickCount();
std::cout << "reading input image and blob" << std::endl;
frame = inputImage.clone();
Mat inpBlob = blobFromImage(frame, 1.0 / 255, Size(inWidth, inHeight), Scalar(0, 0, 0), false, false);
return inpBlob;
}
void OpenPoseVideo::movementSkeleton(Mat inputImage, Mat inpBlob, std::function<void(std::vector<Point>)> f) {
std::cout << "movement skeleton start" << std::endl;
int frameWidth = inputImage.size().width;
int frameHeight = inputImage.size().height;
std::cout << "done reading image and blob" << std::endl;
net.setInput(inpBlob);
std::cout << "done setting input to net" << std::endl;
Mat output = net.forward();
int H = output.size[2];
int W = output.size[3];
std::cout << "about to find position of boxy parts" << std::endl;
// find the position of the body parts
vector<Point> points(nPoints);
for (int n = 0; n < nPoints; n++)
{
// Probability map of corresponding body's part.
Mat probMap(H, W, CV_32F, output.ptr(0, n));
Point2f p(-1, -1);
Point maxLoc;
double prob;
minMaxLoc(probMap, 0, &prob, 0, &maxLoc);
if (prob > thresh)
{
p = maxLoc;
p.x *= (float)frameWidth / W;
p.y *= (float)frameHeight / H;
/*circle(frame, cv::Point((int)p.x, (int)p.y), 8, Scalar(0, 255, 255), -1);
cv::putText(frame, cv::format("%d", n), cv::Point((int)p.x, (int)p.y), cv::FONT_HERSHEY_COMPLEX, 1.1, cv::Scalar(0, 0, 255), 2);*/
}
points[n] = p;
}
//cv::putText(frame, cv::format("time taken = %.2f sec", t), cv::Point(50, 50), cv::FONT_HERSHEY_COMPLEX, .8, cv::Scalar(255, 50, 0), 2);
// imshow("Output-Keypoints", frameCopy);
/*imshow("Output-Skeleton", frame);*/
std::cout << "about to call points receiving method" << std::endl;
f(points);
}
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
using namespace cv;
namespace computervision
{
class OpenPoseVideo{
private:
public:
cv::Mat getBlobFromImage(cv::Mat inputImage);
void movementSkeleton(Mat inputImage, Mat inpBlob, std::function<void(std::vector<Point>)> f);
void setup();
};
}

View File

@@ -0,0 +1,105 @@
#include "SkinDetector.h"
/*
Author: Pierfrancesco Soffritti https://github.com/PierfrancescoSoffritti
*/
namespace computervision
{
SkinDetector::SkinDetector(void) {
hLowThreshold = 0;
hHighThreshold = 0;
sLowThreshold = 0;
sHighThreshold = 0;
vLowThreshold = 0;
vHighThreshold = 0;
calibrated = false;
skinColorSamplerRectangle1, skinColorSamplerRectangle2;
}
void SkinDetector::drawSkinColorSampler(Mat input) {
int frameWidth = input.size().width, frameHeight = input.size().height;
int rectangleSize = 25;
Scalar rectangleColor = Scalar(255, 0, 255);
skinColorSamplerRectangle1 = Rect(frameWidth / 5, frameHeight / 2, rectangleSize, rectangleSize);
skinColorSamplerRectangle2 = Rect(frameWidth / 5, frameHeight / 3, rectangleSize, rectangleSize);
rectangle(
input,
skinColorSamplerRectangle1,
rectangleColor
);
rectangle(
input,
skinColorSamplerRectangle2,
rectangleColor
);
}
void SkinDetector::calibrate(Mat input) {
Mat hsvInput;
cvtColor(input, hsvInput, CV_BGR2HSV);
Mat sample1 = Mat(hsvInput, skinColorSamplerRectangle1);
Mat sample2 = Mat(hsvInput, skinColorSamplerRectangle2);
calculateThresholds(sample1, sample2);
calibrated = true;
}
void SkinDetector::calculateThresholds(Mat sample1, Mat sample2) {
int offsetLowThreshold = 80;
int offsetHighThreshold = 30;
Scalar hsvMeansSample1 = mean(sample1);
Scalar hsvMeansSample2 = mean(sample2);
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;
// 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;
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);
}
}

View File

@@ -0,0 +1,76 @@
#pragma once
#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
*/
namespace computervision
{
using namespace cv;
using namespace std;
class SkinDetector {
public:
SkinDetector(void);
/*
* @brief draws the positions in where the skin color will be sampled.
*
* @param input the input matrix to sample the skin color from
*/
void drawSkinColorSampler(Mat input);
/*
* @brief calibrates the skin color detector with the given input frame
*
* @param input the input frame to calibrate from
*/
void calibrate(Mat input);
/*
* @brief gets the mask for the hand
*
* @param input the input matrix to get the skin mask from
* @returns the skin mask in a new matrix
*/
Mat getSkinMask(Mat input);
private:
// thresholds for hsv calculation
int hLowThreshold = 0;
int hHighThreshold = 0;
int sLowThreshold = 0;
int sHighThreshold = 0;
int vLowThreshold = 0;
int vHighThreshold = 0;
// wether or not the skindetector has calibrated yet.
bool calibrated = false;
// rectangles that get drawn to show where the skin color will be sampled
Rect skinColorSamplerRectangle1, skinColorSamplerRectangle2;
/*
* @brief calculates the skin tresholds for the given samples
*
* @param sample1 the first sample
* @param sample2 the second sample
*/
void calculateThresholds(Mat sample1, Mat sample2);
/**
* @brief the opening. it generates the structuring element and performs the morphological transformations required to detect the hand.
* This needs to be done to get the skin mask.
*
* @param binaryImage the matrix to perform the opening on. This needs to be a binary image, so consisting of only 1's and 0's.
* @param structuralElementShape the shape to use for the kernel that is used with generating the structuring element
* @param structuralElementSize the size of the kernel that will be used with generating the structuring element.
*/
void performOpening(Mat binaryImage, int structuralElementShape, Point structuralElementSize);
};
}

View File

@@ -0,0 +1,33 @@
#include "VideoCapture.h"
#include <mutex>
#include <iostream>
namespace videocapture{
static cv::VideoCapture cap(0);
static std::mutex mtx;
cv::VideoCapture getCap() {
cap.release();
return cap;
}
cv::Mat readFrame()
{
std::cout << "reading frame" << std::endl;
cv::Mat camFrame, videoFrame;
mtx.lock();
bool res = cap.read(camFrame);
std::cout << (res ? "reading worked" : "reading failed") << std::endl;
videoFrame = camFrame.clone();
mtx.unlock();
return videoFrame;
}
std::mutex* getMutex()
{
return &mtx;
}
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>
#include <mutex>
namespace videocapture {
cv::VideoCapture getCap();
std::mutex* getMutex();
cv::Mat readFrame();
}

View File

@@ -0,0 +1,41 @@
#include <iostream>
#include "async_arm_detection.h"
#include "../OpenPoseVideo.h"
#include <thread>
#include "../VideoCapture.h"
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
namespace computervision
{
AsyncArmDetection::AsyncArmDetection()
{
}
void AsyncArmDetection::run_arm_detection()
{
}
void AsyncArmDetection::start(std::function<void(std::vector<Point>)> points_ready_func, OpenPoseVideo op)
{
auto lambda = [](cv::Mat img, std::function<void(std::vector<Point>)> f, OpenPoseVideo op, cv::Mat inpBlob) {
std::cout << "STARTING THREAD LAMBDA" << std::endl;
//imshow("image", img); 255, Size(368, 368), Scalar(0, 0, 0), false, false);
op.movementSkeleton(img, inpBlob, f);
//}
};
cv::Mat img = videocapture::readFrame();
std::cout << "starting function" << std::endl;
cv::Mat inpBlob = op.getBlobFromImage(videocapture::readFrame());
std::thread async_arm_detect_thread(lambda, img, points_ready_func, op, inpBlob);
}
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include <vector>
#include <opencv2/core/types.hpp>
#include <opencv2/videoio.hpp>
#include <functional>
#include "../OpenPoseVideo.h"
namespace computervision
{
class AsyncArmDetection
{
public:
AsyncArmDetection(void);
void start(std::function<void(std::vector<cv::Point>)>, computervision::OpenPoseVideo op);
private:
void run_arm_detection();
};
}

View File

@@ -1,11 +1,16 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/gtc/matrix_transform.hpp>
#include <functional>
#include <vector>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <ostream>
#include <stdlib.h>
#include <opencv2/core.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/video.hpp>
#include "models/model.h"
#include "renderEngine/loader.h"
@@ -14,6 +19,12 @@
#include "shaders/static_shader.h"
#include "toolbox/toolbox.h"
#include "computervision/ObjectDetection.h"
//#include "computervision/OpenPoseImage.h"
#include "computervision/OpenPoseVideo.h"
#include "computervision/async/async_arm_detection.h"
#pragma comment(lib, "glfw3.lib")
#pragma comment(lib, "glew32s.lib")
#pragma comment(lib, "opengl32.lib")
@@ -21,11 +32,19 @@
static double UpdateDelta();
static GLFWwindow* window;
computervision::AsyncArmDetection as;
computervision::OpenPoseVideo openPoseVideo;
void retrieve_points(std::vector<Point> arm_points)
{
std::cout << "got points!!" << std::endl;
std::cout << "points: " << arm_points << std::endl;
as.start(retrieve_points, openPoseVideo);
}
int main(void)
{
#pragma region OPENGL_SETTINGS
#pragma region OPENGL_SETTINGS
if (!glfwInit())
throw "Could not inditialize glwf";
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGT, "SDBA", NULL, NULL);
@@ -37,7 +56,7 @@ int main(void)
glfwMakeContextCurrent(window);
glewInit();
glGetError();
#pragma endregion
#pragma endregion
glfwSetKeyCallback(window, [](GLFWwindow* window, int key, int scancode, int action, int mods)
{
@@ -57,6 +76,24 @@ int main(void)
entities::Camera camera(glm::vec3(0, 0, 0), glm::vec3(0, 0, 0));
// create object detection object instance
computervision::ObjectDetection objDetect;
//computervision::OpenPoseImage openPoseImage;
openPoseVideo.setup();
// set up object detection
//objDetect.setup();
cv::Mat cameraFrame;
//openPoseVideo.setup();
as.start(retrieve_points, openPoseVideo);
// Main game loop
while (!glfwWindowShouldClose(window))
{
@@ -70,8 +107,12 @@ int main(void)
shader.Start();
shader.LoadViewMatrix(camera);
render_engine::renderer::Render(entity, shader);
cameraFrame = objDetect.readCamera();
//objDetect.detectHand(cameraFrame);
// Finish up
shader.Stop();
glfwSwapBuffers(window);

View File

@@ -18,7 +18,7 @@ namespace render_engine
void Init(shaders::StaticShader& shader)
{
const glm::mat4 projectionMatrix =
glm::perspective(glm::radians(FOV), (WINDOW_WIDTH / WINDOW_HEIGT), NEAR_PLANE, FAR_PLANE);
glm::perspective(glm::radians(FOV), (float)(WINDOW_WIDTH / WINDOW_HEIGT), NEAR_PLANE, FAR_PLANE);
shader.Start();
shader.LoadProjectionMatrix(projectionMatrix);

View File

@@ -5,8 +5,8 @@
namespace toolbox
{
#define WINDOW_WIDTH 1400.0f
#define WINDOW_HEIGT 800.0f
#define WINDOW_WIDTH 1400
#define WINDOW_HEIGT 800
glm::mat4 CreateModelMatrix(glm::vec3 translation, glm::vec3 rotation, float scale);

View File

@@ -19,6 +19,14 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\computervision\async\async_arm_detection.cpp" />
<ClCompile Include="src\computervision\FaceDetector.cpp" />
<ClCompile Include="src\computervision\ObjectDetection.cpp" />
<ClCompile Include="src\computervision\OpenPoseVideo.cpp" />
<ClCompile Include="src\computervision\SkinDetector.cpp" />
<ClCompile Include="src\computervision\FingerCount.cpp" />
<ClCompile Include="src\computervision\BackgroundRemover.cpp" />
<ClCompile Include="src\computervision\VideoCapture.cpp" />
<ClCompile Include="src\entities\camera.cpp" />
<ClCompile Include="src\entities\entity.cpp" />
<ClCompile Include="src\main.cpp" />
@@ -30,6 +38,14 @@
<ClCompile Include="src\toolbox\toolbox.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\computervision\async\async_arm_detection.h" />
<ClInclude Include="src\computervision\FaceDetector.h" />
<ClInclude Include="src\computervision\FingerCount.h" />
<ClInclude Include="src\computervision\BackgroundRemover.h" />
<ClInclude Include="src\computervision\OpenPoseVideo.h" />
<ClInclude Include="src\computervision\SkinDetector.h" />
<ClInclude Include="src\computervision\ObjectDetection.h" />
<ClInclude Include="src\computervision\VideoCapture.h" />
<ClInclude Include="src\entities\camera.h" />
<ClInclude Include="src\entities\entity.h" />
<ClInclude Include="src\models\model.h" />
@@ -41,6 +57,14 @@
<ClInclude Include="src\stb_image.h" />
<ClInclude Include="src\toolbox\toolbox.h" />
</ItemGroup>
<ItemGroup>
<Xml Include="res\haarcascade_frontalface_alt.xml" />
</ItemGroup>
<ItemGroup>
<None Include="res\pose\coco\pose_deploy_linevec.prototxt" />
<None Include="res\pose\mpi\pose_deploy_linevec_faster_4_stages.prototxt" />
<None Include="res\pose\mpi\pose_iter_160000.caffemodel" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{A7ECF1BE-DB22-4BF7-BFF6-E3BF72691EE6}</ProjectGuid>

View File

@@ -42,6 +42,30 @@
<ClCompile Include="src\toolbox\toolbox.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\computervision\ObjectDetection.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\computervision\SkinDetector.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\computervision\FingerCount.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\computervision\FaceDetector.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\computervision\BackgroundRemover.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\computervision\OpenPoseVideo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\computervision\async\async_arm_detection.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\computervision\VideoCapture.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\entities\Camera.h">
@@ -74,5 +98,37 @@
<ClInclude Include="src\toolbox\toolbox.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\computervision\ObjectDetection.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\computervision\SkinDetector.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\computervision\FingerCount.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\computervision\FaceDetector.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\computervision\BackgroundRemover.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\computervision\OpenPoseVideo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\computervision\async\async_arm_detection.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\computervision\VideoCapture.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Xml Include="res\haarcascade_frontalface_alt.xml" />
</ItemGroup>
<ItemGroup>
<None Include="res\pose\coco\pose_deploy_linevec.prototxt" />
<None Include="res\pose\mpi\pose_deploy_linevec_faster_4_stages.prototxt" />
<None Include="res\pose\mpi\pose_iter_160000.caffemodel" />
</ItemGroup>
</Project>