The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
#!/usr/bin/perl
# -*- mode: perl; coding: utf-8; tab-width: 4; -*-

use strict;
use warnings;
use lib qw(blib/lib blib/arch);
use Cv;

use File::Basename;

my $haarDir = "/usr/local/share/OpenCV/haarcascades";

sub help {
	die << "----";

This program demonstrates the haar cascade recognizer this classifier
can recognize many ~rigid objects, it\'s most known use is for faces.

Usage:
\$ .\/facedetect
	[--cascade=<cascade_path>]
		this is the primary trained classifier such as frontal face
	[--nested-cascade[=<nested_cascade_path>]]
		this an optional secondary classifier such as eyes
	[--scale=<image scale greater or equal to 1, try 1.3 for example>]
	[filename|camera_index]

see facedetect.cmd for one call:
\$ .\/facedetect
	--cascade=$haarDir/haarcascade_frontalface_alt.xml
	--nested-cascade=$haarDir/haarcascade_eye.xml
	--scale=1.3

Hit any key to quit.
----
	 ;
}

my $cascadeName       = "$haarDir/haarcascade_frontalface_alt.xml";
my $nestedCascadeName = "$haarDir/haarcascade_eye_tree_eyeglasses.xml";
my $scale = 1;

use Getopt::Long;
unless (GetOptions(
	"--cascade=s" => \$cascadeName,
	"--nested-cascade=s" => \$nestedCascadeName,
	"--scale=s" => \$scale,
		)) {
	help();
}

my $inputName = shift(@ARGV);
my $cascade = Cv->Load($cascadeName);
unless ($cascade) {
	die "ERROR: Could not load classifier cascade";
}
my $nestedCascade = Cv->Load($nestedCascadeName);
unless ($nestedCascade) {
	warn "WARNING: Could not load classifier cascade for nested objects\n";
}

my $capture;
my $image;
if (defined $inputName && $inputName =~ /^\d$/) {
    $capture = Cv->captureFromCAM($inputName);
	unless ($capture) {
		warn "Capture from CAM $inputName didn't work\n";
	}
} elsif ($inputName) {
	$image = Cv->loadImage($inputName);
	unless ($image) {
		$capture = Cv->captureFromFile($inputName);
		unless ($capture) {
			warn "Capture from AVI $inputName didn't work\n";
		}
	}
}
unless ($capture || $image) {
	my $lena = dirname($0) . "/lena.jpg";
	$image = Cv->loadImage($lena);
}

Cv->NamedWindow("result", 0);

if ($capture) {
	while (my $frame = $capture->queryFrame) {
		my $frameCopy = $frame->flip(\0, 1)->clone;
		$frameCopy->detectAndDraw($cascade, $nestedCascade, $scale);
		last if Cv->waitKey(10) > 0;
	}
} elsif ($image) {
	$image->detectAndDraw($cascade, $nestedCascade, $scale);
	Cv->waitKey;
}

sub Cv::Arr::detectAndDraw {
	my ($img, $cascade, $nestedCascade, $scale) = @_;
    my @colors = (
		CV_RGB(  0,   0, 255),
        CV_RGB(  0, 128, 255),
        CV_RGB(  0, 255, 255),
        CV_RGB(  0, 255,   0),
        CV_RGB(255, 128,   0),
        CV_RGB(255, 255,   0),
        CV_RGB(255,   0,   0),
        CV_RGB(255,   0, 255)
		);
    my $gray = $img->cvtColor(CV_BGR2GRAY);
	my $smallSize = [map { cvRound($_ / $scale) } @{$img->sizes}];
	my $smallImg = $img->new($smallSize, CV_8UC1);
	$gray->resize($smallImg, CV_INTER_LINEAR);
	$smallImg->equalizeHist($smallImg);
	my $storage = Cv->createMemStorage();
    my $t = Cv->getTickCount();

	{ package Cv::Seq::FaceDetect;
	  our @ISA = qw(Cv::Seq::Point);
	  sub template {
		  my $self = CORE::shift;
		  my ($t, $c) = ("i5", 5);
		  wantarray? ($t, $c) : $t;
	  }
	}
	my $faces = bless $smallImg->HaarDetectObjects(
		$cascade, $storage, 1.1, 2, 0
        # |CV_HAAR_FIND_BIGGEST_OBJECT
        # |CV_HAAR_DO_ROUGH_SEARCH
        |CV_HAAR_SCALE_IMAGE
		,
        cvSize(30, 30)
		), "Cv::Seq::FaceDetect";
    $t = Cv->getTickCount() - $t;
    printf("detection time = %g ms\n", $t/(Cv->getTickFrequency()*1000));
	my $i = 0;
	for my $face (@$faces) {
		my $color = $colors[$i++ % @colors];
		my ($x, $y, $w, $h) = @$face;
		my $center = [
			cvRound(($x + $w * 0.5) * $scale),
			cvRound(($y + $h * 0.5) * $scale),
			];
		my $radius = cvRound(($w + $h) * 0.25 * $scale);
        $img->circle($center, $radius, $color, 3, 8, 0);
		next unless $nestedCascade;
        $smallImg->ROI([$x, $y, $w, $h]);

		my $nestedObjects = bless $smallImg->haarDetectObjects(
			$nestedCascade, $storage, 1.1, 2, 0
            # |CV_HAAR_FIND_BIGGEST_OBJECT
            # |CV_HAAR_DO_ROUGH_SEARCH
            # |CV_HAAR_DO_CANNY_PRUNING
            |CV_HAAR_SCALE_IMAGE
			,
			cvSize(30, 30)
			), "Cv::Seq::FaceDetect";
        $smallImg->resetROI;
		foreach my $eye (@$nestedObjects) {
			my ($nx, $ny, $nw, $nh) = @$eye;
			my $center = [
				cvRound(($x + $nx + $nw * 0.5) * $scale),
				cvRound(($y + $ny + $nh * 0.5) * $scale),
				];
			my $radius = cvRound(($nw + $nh) * 0.25 * $scale);
			# $img->circle($center, $radius, $color, 3, 8, 0);
			$img->rectangle(
				[$x + $nx, $y + $ny], [$x + $nx + $nw, $y + $ny + $nh ], 
				$color, 3, 8, 0);
		}
	}
	$img->show("result");
}