B
After much discussion I finally understood that what you wanted
do (and it was not by your merit, say in passing; therefore it is the
Tip: In the future, look to focus on the problem rather than focus on a
solution you think that works).What you want is not to run the Cascade within a limited area (until this makes no sense!). What you want is to know if the area of an object, as detected by Cascade (in the case, the region where a human face is located) is or not within another area marked by the user.As the Cascade area is a ROI (which is rectangular by principle, since it is a sub-area of an image) and its verification area is a polygon with more sides, this comparison is not trivial. As both polygons are closed, you could make a simple check of the type "is completely inside or is completely out" simply checking whether all ROI vertices are or not within the polygon limits. I could also do a "collision" check, as the games do (and have http://devmag.org.za/2009/04/13/basic-collision-detection-in-2d-part-1/ ). However, you don't need to know if one area "touched" on the other, but if one contains the other. Therefore, using potentially colliding techniques would produce many false positives.As you use OpenCV, a very simple and effective way is you do so:Draw each area in a distinct binary image (i.e. in an image that has only black and white), filling the area of the region completely blank.Make a logical "E" of both images, pixel to pixel. Pixels all in black are worth 0, and all blank pixels are worth 1. How 0 E 0 or 0 E 1 result in 0, only the pixels that are 1 (white) in both images will be kept in the resulting image.Count the blank pixels (non-zero) in the resulting image, and divide that value by the number of pixels in the originally detected area. This is a percentage, which therefore varies between 0 and 1. A detected ROI that is fully inside your polygon will result in 1 (i.e. 100% in!) and a ROI that is totally out of your polygon will result in 0 (i.e. 100% out).With the possession of this value, create some decision criterion to consider as "invasion" or not (for example, consider as invaded only when the given value is greater than 0.7, i.e. 70%).The code#include <opencv2\core.hpp>
#include <opencv2\highgui.hpp>
#include <opencv2\imgproc.hpp>
#include <vector>
#include <iostream>
using namespace std;
using namespace cv;
Size sz = Size(800, 600);
Mat image = Mat::zeros(sz, CV_8UC3);
Rect roi;
vector<Point> polygon;
bool buttonDown = false;
typedef enum
{
CAPTURING_POLYGON,
CAPTURING_ROI
} Capturing;
Capturing cap;
void roiCapture(int event, int x, int y, int flags, void *userdata)
{
if(event == EVENT_LBUTTONDOWN)
{
roi = Rect(x, y, 0, 0);
buttonDown = true;
}
else if(event == EVENT_LBUTTONUP)
{
buttonDown = false;
roi = Rect(Point(roi.x, roi.y), Point(x, y));
}
else if(buttonDown)
roi = Rect(Point(roi.x, roi.y), Point(x, y));
}
void polygonCapture(int event, int x, int y, int flags, void *userdata)
{
if(event == EVENT_LBUTTONUP)
polygon.push_back(Point(x, y));
else if(event == EVENT_RBUTTONDOWN)
polygon.clear();
}
float calcOverlapping(const Rect &roi, const vector<Point> &polygon, const Size &sz, Mat &resp)
{
// Desenha o polígono preenchido com branco em uma imagem binária toda em preto
Mat imgPol = Mat::zeros(sz, CV_8UC1);
vector<vector<Point>> points;
points.push_back(polygon);
int npts = polygon.size();
fillPoly(imgPol, points, Scalar(255));
// Desenha o retângulo preenchido com branco em uma imagem binária toda em preto
Mat imgRoi = Mat::zeros(sz, CV_8UC1);
rectangle(imgRoi, roi, Scalar(255, 255, 255), CV_FILLED);
// Faz um "E" lógico das imagens. Isso causa uma interseção das áreas, pois apenas os pixels
// que forem branco NAS DUAS imagens irão permanecer em branco na imagem de resposta.
bitwise_and(imgPol, imgRoi, resp);
// Conta os pixels que são diferentes de preto (0) na imagem resultante
int intersec = countNonZero(resp);
// Conta os pixels que são diferentes de preto (0) na imagem da ROI (porque a área dela é
// usada como base (isto é, que se saber qual é a porcentagem da ROI que está dentro do
// polígono)
int base = countNonZero(imgRoi);
// A resposta é um percentual (entre 0 e 1), indicando quanto da ROI está na interseção.
// Se a ROI estiver inteira dentro do polígono, a interseção vai ser total e esse valor vai
// dar 1. Se estiver totalmente fora, a interseção vai ser nula e esse valor vai dar 0.
return float(intersec) / float(base);
}
int main()
{
// ===========================================
// Captura das áreas para teste.
// Essa parte você já faz (ou sabe fazer).
// ===========================================
namedWindow("Captura", WINDOW_AUTOSIZE);
int fontFace = FONT_HERSHEY_SIMPLEX;
double fontScale = 0.5;
int thickness = 1;
// Começa capturando a área do polígono
cap = CAPTURING_POLYGON;
setMouseCallback("Captura", polygonCapture, NULL);
bool ok = true;
while(ok)
{
// Pinta tudo de preto
image = Scalar(0, 0, 0);
// Desenha o texto de ajuda
string text;
if(cap == CAPTURING_ROI)
text = "Desenhe o ROI com o mouse. Pressione [C] para continuar.";
else
text = "Adicione pontos ao POLIGONO com o mouse (botao direito limpa). Pressione [C] para continuar.";
putText(image, text, Point(0, 15), fontFace, fontScale, Scalar(255, 255, 255), thickness);
// Desenha o polígono, se ele tiver dados
if(polygon.size() == 1)
circle(image, polygon[0], 1, Scalar(255, 0, 255));
else if(polygon.size() > 1)
{
vector<vector<Point>> points;
points.push_back(polygon);
int npts = polygon.size();
fillPoly(image, points, Scalar(255, 0, 255));
}
// Desenha a ROI, se ela tiver dados
if(roi.width != 0 || roi.height != 0)
rectangle(image, roi, Scalar(0, 255, 255));
imshow("Captura", image);
int k = waitKey(10);
switch(k)
{
case 27: // ESC
case 'q':
case 'Q':
destroyAllWindows();
return 0;
case 'c':
case 'C':
if(cap == CAPTURING_POLYGON)
{
cap = CAPTURING_ROI;
// Troca pra captura da ROI
setMouseCallback("Captura", roiCapture, NULL);
}
else
ok = false;
break;
}
}
// ===========================================
// Cálculo do percentual de interseção.
// Essa parte é a novidade.
// ===========================================
Mat resp;
float val = calcOverlapping(roi, polygon, sz, resp);
cout << "Valor de intereseção calculado: " << val << endl;
imshow("Debug", resp);
waitKey(0);
return 0;
}
The really important function is calcOverlapping, which makes the "magic". Results ExamplesIn the following examples, the left image indicates the ROI (yellow) and the polygon (in magenta), and the right image indicates only the intersection area in the binary image resulting from the logical E between the two binary images built from the figures in the left image. Valor de interseção calculado: 0.748836
Valor de interseção calculado: 0.012016
Edition: Example of Real UseYou created it Well, if you want the people detector (Cascade) to find only people within the region delimited by the polygon in red, just use what I explained above and make the logical operation to have only the pixels of the polygon region in a ROI that remains rectangular (the "end" image in the example below). Then apply Cascade in this new image. Example (in Python this time, because I did in a hurry - but it's the same thing in C++):import numpy as np
import cv2
image = cv2.imread('teste.png', cv2.IMREAD_COLOR)
Define a Região de Interesse (ROI)
left = 15
top = 118
width = 615
height = 365
roi = image[top:height, left:width]
Define o polígono delimitador
points = np.array([[(0, 184), (165, 40), (402, 0), (598, 20), (482, 56), (342, 245)]])
mask = np.zeros(roi.shape, roi.dtype)
cv2.fillPoly(mask, points, (255, 255, 255)) # Preenche com branco (note que a cor é RGB - i.e. 3 bandas, já que a imagem não é binária!)
final = cv2.bitwise_and(roi, mask)
cv2.imshow('Imagem Original', image)
cv2.imshow('ROI', roi)
cv2.imshow('Mascara', mask)
cv2.imshow('Final', final)
cv2.waitKey(0)
Result: