// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
# include "test_precomp.hpp"
namespace opencv_test { namespace {
// label format:
// image_name
// num_face
// face_1
// face_..
// face_num
std : : map < std : : string , Mat > blobFromTXT ( const std : : string & path , int numCoords )
{
std : : ifstream ifs ( path . c_str ( ) ) ;
CV_Assert ( ifs . is_open ( ) ) ;
std : : map < std : : string , Mat > gt ;
Mat faces ;
int faceNum = - 1 ;
int faceCount = 0 ;
for ( std : : string line , key ; getline ( ifs , line ) ; )
{
std : : istringstream iss ( line ) ;
if ( line . find ( " .png " ) ! = std : : string : : npos )
{
// Get filename
iss > > key ;
}
else if ( line . find ( " " ) = = std : : string : : npos )
{
// Get the number of faces
iss > > faceNum ;
}
else
{
// Get faces
Mat face ( 1 , numCoords , CV_32FC1 ) ;
for ( int j = 0 ; j < numCoords ; j + + )
{
iss > > face . at < float > ( 0 , j ) ;
}
faces . push_back ( face ) ;
faceCount + + ;
}
if ( faceCount = = faceNum )
{
// Store faces
gt [ key ] = faces ;
faces . release ( ) ;
faceNum = - 1 ;
faceCount = 0 ;
}
}
return gt ;
}
TEST ( Objdetect_face_detection , regression )
{
// Pre-set params
float scoreThreshold = 0.7f ;
float matchThreshold = 0.9f ;
float l2disThreshold = 5.0f ;
int numLM = 5 ;
int numCoords = 4 + 2 * numLM ;
// Load ground truth labels
std : : map < std : : string , Mat > gt = blobFromTXT ( findDataFile ( " dnn_face/detection/cascades_labels.txt " ) , numCoords ) ;
// for (auto item: gt)
// {
// std::cout << item.first << " " << item.second.size() << std::endl;
// }
// Initialize detector
std : : string model = findDataFile ( " dnn/onnx/models/yunet-202202.onnx " , false ) ;
Ptr < FaceDetectorYN > faceDetector = FaceDetectorYN : : create ( model , " " , Size ( 300 , 300 ) ) ;
faceDetector - > setScoreThreshold ( 0.7f ) ;
// Detect and match
for ( auto item : gt )
{
std : : string imagePath = findDataFile ( " cascadeandhog/images/ " + item . first ) ;
Mat image = imread ( imagePath ) ;
// Set input size
faceDetector - > setInputSize ( image . size ( ) ) ;
// Run detection
Mat faces ;
faceDetector - > detect ( image , faces ) ;
// std::cout << item.first << " " << item.second.rows << " " << faces.rows << std::endl;
// Match bboxes and landmarks
std : : vector < bool > matchedItem ( item . second . rows , false ) ;
for ( int i = 0 ; i < faces . rows ; i + + )
{
if ( faces . at < float > ( i , numCoords ) < scoreThreshold )
continue ;
bool boxMatched = false ;
std : : vector < bool > lmMatched ( numLM , false ) ;
cv : : Rect2f resBox ( faces . at < float > ( i , 0 ) , faces . at < float > ( i , 1 ) , faces . at < float > ( i , 2 ) , faces . at < float > ( i , 3 ) ) ;
for ( int j = 0 ; j < item . second . rows & & ! boxMatched ; j + + )
{
if ( matchedItem [ j ] )
continue ;
// Retrieve bbox and compare IoU
cv : : Rect2f gtBox ( item . second . at < float > ( j , 0 ) , item . second . at < float > ( j , 1 ) , item . second . at < float > ( j , 2 ) , item . second . at < float > ( j , 3 ) ) ;
double interArea = ( resBox & gtBox ) . area ( ) ;
double iou = interArea / ( resBox . area ( ) + gtBox . area ( ) - interArea ) ;
if ( iou > = matchThreshold )
{
boxMatched = true ;
matchedItem [ j ] = true ;
}
// Match landmarks if bbox is matched
if ( ! boxMatched )
continue ;
for ( int lmIdx = 0 ; lmIdx < numLM ; lmIdx + + )
{
float gtX = item . second . at < float > ( j , 4 + 2 * lmIdx ) ;
float gtY = item . second . at < float > ( j , 4 + 2 * lmIdx + 1 ) ;
float resX = faces . at < float > ( i , 4 + 2 * lmIdx ) ;
float resY = faces . at < float > ( i , 4 + 2 * lmIdx + 1 ) ;
float l2dis = cv : : sqrt ( ( gtX - resX ) * ( gtX - resX ) + ( gtY - resY ) * ( gtY - resY ) ) ;
if ( l2dis < = l2disThreshold )
{
lmMatched [ lmIdx ] = true ;
}
}
}
EXPECT_TRUE ( boxMatched ) < < " In image " < < item . first < < " , cannot match resBox " < < resBox < < " with any ground truth. " ;
if ( boxMatched )
{
EXPECT_TRUE ( std : : all_of ( lmMatched . begin ( ) , lmMatched . end ( ) , [ ] ( bool v ) { return v ; } ) ) < < " In image " < < item . first < < " , resBox " < < resBox < < " matched but its landmarks failed to match. " ;
}
}
}
}
TEST ( Objdetect_face_recognition , regression )
{
// Pre-set params
float score_thresh = 0.9f ;
float nms_thresh = 0.3f ;
double cosine_similar_thresh = 0.363 ;
double l2norm_similar_thresh = 1.128 ;
// Load ground truth labels
std : : ifstream ifs ( findDataFile ( " dnn_face/recognition/cascades_label.txt " ) . c_str ( ) ) ;
CV_Assert ( ifs . is_open ( ) ) ;
std : : set < std : : string > fSet ;
std : : map < std : : string , Mat > featureMap ;
std : : map < std : : pair < std : : string , std : : string > , int > gtMap ;
for ( std : : string line , key ; getline ( ifs , line ) ; )
{
std : : string fname1 , fname2 ;
int label ;
std : : istringstream iss ( line ) ;
iss > > fname1 > > fname2 > > label ;
// std::cout<<fname1<<" "<<fname2<<" "<<label<<std::endl;
fSet . insert ( fname1 ) ;
fSet . insert ( fname2 ) ;
gtMap [ std : : make_pair ( fname1 , fname2 ) ] = label ;
}
// Initialize detector
std : : string detect_model = findDataFile ( " dnn/onnx/models/yunet-202202.onnx " , false ) ;
Ptr < FaceDetectorYN > faceDetector = FaceDetectorYN : : create ( detect_model , " " , Size ( 150 , 150 ) , score_thresh , nms_thresh ) ;
std : : string recog_model = findDataFile ( " dnn/onnx/models/face_recognizer_fast.onnx " , false ) ;
Ptr < FaceRecognizerSF > faceRecognizer = FaceRecognizerSF : : create ( recog_model , " " ) ;
// Detect and match
for ( auto fname : fSet )
{
std : : string imagePath = findDataFile ( " dnn_face/recognition/ " + fname ) ;
Mat image = imread ( imagePath ) ;
Mat faces ;
faceDetector - > detect ( image , faces ) ;
Mat aligned_face ;
faceRecognizer - > alignCrop ( image , faces . row ( 0 ) , aligned_face ) ;
Mat feature ;
faceRecognizer - > feature ( aligned_face , feature ) ;
featureMap [ fname ] = feature . clone ( ) ;
}
for ( auto item : gtMap )
{
Mat feature1 = featureMap [ item . first . first ] ;
Mat feature2 = featureMap [ item . first . second ] ;
int label = item . second ;
double cos_score = faceRecognizer - > match ( feature1 , feature2 , FaceRecognizerSF : : DisType : : FR_COSINE ) ;
double L2_score = faceRecognizer - > match ( feature1 , feature2 , FaceRecognizerSF : : DisType : : FR_NORM_L2 ) ;
EXPECT_TRUE ( label = = 0 ? cos_score < = cosine_similar_thresh : cos_score > cosine_similar_thresh ) < < " Cosine match result of images " < < item . first . first < < " and " < < item . first . second < < " is different from ground truth (score: " < < cos_score < < " ;Thresh: " < < cosine_similar_thresh < < " ). " ;
EXPECT_TRUE ( label = = 0 ? L2_score > l2norm_similar_thresh : L2_score < = l2norm_similar_thresh ) < < " L2norm match result of images " < < item . first . first < < " and " < < item . first . second < < " is different from ground truth (score: " < < L2_score < < " ;Thresh: " < < l2norm_similar_thresh < < " ). " ;
}
}
} } // namespace