Friday 21 June 2013

OpenCV Python sample programs - Face Detect etc

I am curious of how difficult to write a face detect program.  So I read the sample code and found it only about 100 Python statements long!  So it is very hobbyist friendly.

I also find the camera capture image function as simple as Pygame.



*** OpenCV Python sample programs H:\fongvision|OpenCV-2.3.1\samples\python\ ***



# ****************************************************************************

# camera.py
# ****************************************************************************

import cv2.cv as cv

import time

cv.NamedWindow("camera", 1)


capture = cv.CaptureFromCAM(0)


while True:

    img = cv.QueryFrame(capture)
    cv.ShowImage("camera", img)
    if cv.WaitKey(10) == 27:
        break


# ****************************************************************************

# cv20squartes.py
# ****************************************************************************

"""

Find Squares in image by finding countours and filtering
"""
#Results slightly different from C version on same images, but is 
#otherwise ok

import math

import cv2.cv as cv

def angle(pt1, pt2, pt0):

    "calculate angle contained by 3 points(x, y)"
    dx1 = pt1[0] - pt0[0]
    dy1 = pt1[1] - pt0[1]
    dx2 = pt2[0] - pt0[0]
    dy2 = pt2[1] - pt0[1]

    nom = dx1*dx2 + dy1*dy2

    denom = math.sqrt( (dx1*dx1 + dy1*dy1) * (dx2*dx2 + dy2*dy2) + 1e-10 )
    ang = nom / denom
    return ang

def is_square(contour):

    """
    Squareness checker

    Square contours should:

        -have 4 vertices after approximation, 
        -have relatively large area (to filter out noisy contours)
        -be convex.
        -have angles between sides close to 90deg (cos(ang) ~0 )
    Note: absolute value of an area is used because area may be
    positive or negative - in accordance with the contour orientation
    """

    area = math.fabs( cv.ContourArea(contour) )

    isconvex = cv.CheckContourConvexity(contour)
    s = 0
    if len(contour) == 4 and area > 1000 and isconvex:
        for i in range(1, 4):
            # find minimum angle between joint edges (maximum of cosine)
            pt1 = contour[i]
            pt2 = contour[i-1]
            pt0 = contour[i-2]

            t = math.fabs(angle(pt0, pt1, pt2))

            if s <= t:s = t

        # if cosines of all angles are small (all angles are ~90 degree) 

        # then its a square
        if s < 0.3:return True

    return False       


def find_squares_from_binary( gray ):

    """
    use contour search to find squares in binary image
    returns list of numpy arrays containing 4 points
    """
    squares = []
    storage = cv.CreateMemStorage(0)
    contours = cv.FindContours(gray, storage, cv.CV_RETR_TREE, cv.CV_CHAIN_APPROX_SIMPLE, (0,0))  
    storage = cv.CreateMemStorage(0)
    while contours:
        #approximate contour with accuracy proportional to the contour perimeter
        arclength = cv.ArcLength(contours)
        polygon = cv.ApproxPoly( contours, storage, cv.CV_POLY_APPROX_DP, arclength * 0.02, 0)
        if is_square(polygon):
            squares.append(polygon[0:4])
        contours = contours.h_next()

    return squares


def find_squares4(color_img):

    """
    Finds multiple squares in image

    Steps:

    -Use Canny edge to highlight contours, and dilation to connect
    the edge segments.
    -Threshold the result to binary edge tokens
    -Use cv.FindContours: returns a cv.CvSequence of cv.CvContours
    -Filter each candidate: use Approx poly, keep only contours with 4 vertices, 
    enough area, and ~90deg angles.

    Return all squares contours in one flat list of arrays, 4 x,y points each.

    """
    #select even sizes only
    width, height = (color_img.width & -2, color_img.height & -2 )
    timg = cv.CloneImage( color_img ) # make a copy of input image
    gray = cv.CreateImage( (width,height), 8, 1 )

    # select the maximum ROI in the image

    cv.SetImageROI( timg, (0, 0, width, height) )

    # down-scale and upscale the image to filter out the noise

    pyr = cv.CreateImage( (width/2, height/2), 8, 3 )
    cv.PyrDown( timg, pyr, 7 )
    cv.PyrUp( pyr, timg, 7 )

    tgray = cv.CreateImage( (width,height), 8, 1 )

    squares = []

    # Find squares in every color plane of the image

    # Two methods, we use both:
    # 1. Canny to catch squares with gradient shading. Use upper threshold
    # from slider, set the lower to 0 (which forces edges merging). Then
    # dilate canny output to remove potential holes between edge segments.
    # 2. Binary thresholding at multiple levels
    N = 11
    for c in [0, 1, 2]:
        #extract the c-th color plane
        cv.SetImageCOI( timg, c+1 );
        cv.Copy( timg, tgray, None );
        cv.Canny( tgray, gray, 0, 50, 5 )
        cv.Dilate( gray, gray)
        squares = squares + find_squares_from_binary( gray )

        # Look for more squares at several threshold levels

        for l in range(1, N):
            cv.Threshold( tgray, gray, (l+1)*255/N, 255, cv.CV_THRESH_BINARY )
            squares = squares + find_squares_from_binary( gray )

    return squares



RED = (0,0,255)

GREEN = (0,255,0)
def draw_squares( color_img, squares ):
    """
    Squares is py list containing 4-pt numpy arrays. Step through the list
    and draw a polygon for each 4-group
    """
    color, othercolor = RED, GREEN
    for square in squares:
        cv.PolyLine(color_img, [square], True, color, 3, cv.CV_AA, 0)
        color, othercolor = othercolor, color

    cv.ShowImage(WNDNAME, color_img)



WNDNAME = "Squares Demo"
def main():
    """Open test color images, create display window, start the search"""
    cv.NamedWindow(WNDNAME, 1)
    for name in [ "../c/pic%d.png" % i for i in [1, 2, 3, 4, 5, 6] ]:
        img0 = cv.LoadImage(name, 1)
        try:
            img0
        except ValueError:
            print "Couldn't load %s\n" % name
            continue

        # slider deleted from C version, same here and use fixed Canny param=50

        img = cv.CloneImage(img0)

        cv.ShowImage(WNDNAME, img)


        # force the image processing

        draw_squares( img, find_squares4( img ) )
        
        # wait for key.
        if cv.WaitKey(-1) % 0x100 == 27:
            break

if __name__ == "__main__":

    main()    

# ****************************************************************************

# drawing.py
# ****************************************************************************

#! /usr/bin/env python

from random import Random
import colorsys

print "OpenCV Python version of drawing"


import cv2.cv as cv


def random_color(random):

    """
    Return a random color
    """
    icolor = random.randint(0, 0xFFFFFF)
    return cv.Scalar(icolor & 0xff, (icolor >> 8) & 0xff, (icolor >> 16) & 0xff)

if __name__ == '__main__':


    # some "constants"

    width = 1000
    height = 700
    window_name = "Drawing Demo"
    number = 100
    delay = 5
    line_type = cv.CV_AA  # change it to 8 to see non-antialiased graphics
    
    # create the source image
    image = cv.CreateImage( (width, height), 8, 3)

    # create window and display the original picture in it

    cv.NamedWindow(window_name, 1)
    cv.SetZero(image)
    cv.ShowImage(window_name, image)

    # create the random number

    random = Random()

    # draw some lines

    for i in range(number):
        pt1 =  (random.randrange(-width, 2 * width),
                          random.randrange(-height, 2 * height))
        pt2 =  (random.randrange(-width, 2 * width),
                          random.randrange(-height, 2 * height))
        cv.Line(image, pt1, pt2,
                   random_color(random),
                   random.randrange(0, 10),
                   line_type, 0)
        
        cv.ShowImage(window_name, image)
        cv.WaitKey(delay)

    # draw some rectangles

    for i in range(number):
        pt1 =  (random.randrange(-width, 2 * width),
                          random.randrange(-height, 2 * height))
        pt2 =  (random.randrange(-width, 2 * width),
                          random.randrange(-height, 2 * height))
        cv.Rectangle(image, pt1, pt2,
                        random_color(random),
                        random.randrange(-1, 9),
                        line_type, 0)
        
        cv.ShowImage(window_name, image)
        cv.WaitKey(delay)

    # draw some ellipes

    for i in range(number):
        pt1 =  (random.randrange(-width, 2 * width),
                          random.randrange(-height, 2 * height))
        sz =  (random.randrange(0, 200),
                        random.randrange(0, 200))
        angle = random.randrange(0, 1000) * 0.180
        cv.Ellipse(image, pt1, sz, angle, angle - 100, angle + 200,
                        random_color(random),
                        random.randrange(-1, 9),
                        line_type, 0)
        
        cv.ShowImage(window_name, image)
        cv.WaitKey(delay)

    # init the list of polylines

    nb_polylines = 2
    polylines_size = 3
    pt = [0,] * nb_polylines
    for a in range(nb_polylines):
        pt [a] = [0,] * polylines_size

    # draw some polylines

    for i in range(number):
        for a in range(nb_polylines):
            for b in range(polylines_size):
                pt [a][b] =  (random.randrange(-width, 2 * width),
                                     random.randrange(-height, 2 * height))
        cv.PolyLine(image, pt, 1,
                       random_color(random),
                       random.randrange(1, 9),
                       line_type, 0)

        cv.ShowImage(window_name, image)

        cv.WaitKey(delay)

    # draw some filled polylines

    for i in range(number):
        for a in range(nb_polylines):
            for b in range(polylines_size):
                pt [a][b] =  (random.randrange(-width, 2 * width),
                                     random.randrange(-height, 2 * height))
        cv.FillPoly(image, pt,
                       random_color(random),
                       line_type, 0)

        cv.ShowImage(window_name, image)

        cv.WaitKey(delay)

    # draw some circles

    for i in range(number):
        pt1 =  (random.randrange(-width, 2 * width),
                          random.randrange(-height, 2 * height))
        cv.Circle(image, pt1, random.randrange(0, 300),
                     random_color(random),
                     random.randrange(-1, 9),
                     line_type, 0)
        
        cv.ShowImage(window_name, image)
        cv.WaitKey(delay)

    # draw some text

    for i in range(number):
        pt1 =  (random.randrange(-width, 2 * width),
                          random.randrange(-height, 2 * height))
        font = cv.InitFont(random.randrange(0, 8),
                              random.randrange(0, 100) * 0.05 + 0.01,
                              random.randrange(0, 100) * 0.05 + 0.01,
                              random.randrange(0, 5) * 0.1,
                              random.randrange(0, 10),
                              line_type)

        cv.PutText(image, "Testing text rendering!",

                      pt1, font,
                      random_color(random))
        
        cv.ShowImage(window_name, image)
        cv.WaitKey(delay)

    # prepare a text, and get it's properties

    font = cv.InitFont(cv.CV_FONT_HERSHEY_COMPLEX,
                          3, 3, 0.0, 5, line_type)
    text_size, ymin = cv.GetTextSize("OpenCV forever!", font)
    pt1 = ((width - text_size[0]) / 2, (height + text_size[1]) / 2)
    image2 = cv.CloneImage(image)

    # now, draw some OpenCV pub ;-)

    for i in range(0, 512, 2):
        cv.SubS(image2, cv.ScalarAll(i), image)
        (r, g, b) = colorsys.hsv_to_rgb((i % 100) / 100., 1, 1)
        cv.PutText(image, "OpenCV forever!",
                      pt1, font, cv.RGB(255 * r, 255 * g, 255 * b))
        cv.ShowImage(window_name, image)
        cv.WaitKey(delay)

    # wait some key to end

    cv.WaitKey(0)

# ****************************************************************************

# edge.py
# ****************************************************************************

#! /usr/bin/env python


print "OpenCV Python version of edge"


import sys

import urllib2
import cv2.cv as cv

# some definitions

win_name = "Edge"
trackbar_name = "Threshold"

# the callback on the trackbar

def on_trackbar(position):

    cv.Smooth(gray, edge, cv.CV_BLUR, 3, 3, 0)

    cv.Not(gray, edge)

    # run the edge dector on gray scale

    cv.Canny(gray, edge, position, position * 3, 3)

    # reset

    cv.SetZero(col_edge)

    # copy edge points

    cv.Copy(im, col_edge, edge)
    
    # show the im
    cv.ShowImage(win_name, col_edge)

if __name__ == '__main__':

    if len(sys.argv) > 1:
        im = cv.LoadImage( sys.argv[1], cv.CV_LOAD_IMAGE_COLOR)
    else:
        url = 'https://code.ros.org/svn/opencv/trunk/opencv/samples/c/fruits.jpg'
        filedata = urllib2.urlopen(url).read()
        imagefiledata = cv.CreateMatHeader(1, len(filedata), cv.CV_8UC1)
        cv.SetData(imagefiledata, filedata, len(filedata))
        im = cv.DecodeImage(imagefiledata, cv.CV_LOAD_IMAGE_COLOR)

    # create the output im

    col_edge = cv.CreateImage((im.width, im.height), 8, 3)

    # convert to grayscale

    gray = cv.CreateImage((im.width, im.height), 8, 1)
    edge = cv.CreateImage((im.width, im.height), 8, 1)
    cv.CvtColor(im, gray, cv.CV_BGR2GRAY)

    # create the window

    cv.NamedWindow(win_name, cv.CV_WINDOW_AUTOSIZE)

    # create the trackbar

    cv.CreateTrackbar(trackbar_name, win_name, 1, 100, on_trackbar)

    # show the im

    on_trackbar(0)

    # wait a key pressed to end

    cv.WaitKey(0)


# ****************************************************************************

# facedetect.py
# ****************************************************************************

#!/usr/bin/python

"""
This program is demonstration for face and object detection using haar-like features.

The program finds faces in a camera image or video stream and displays a red box around them.


Original C implementation by:  ?

Python implementation by: Roman Stanchak, James Bowman
"""
import sys
import cv2.cv as cv
from optparse import OptionParser

# Parameters for haar detection

# From the API:
# The default parameters (scale_factor=2, min_neighbors=3, flags=0) are tuned 
# for accurate yet slow object detection. For a faster operation on real video 
# images the settings are: 
# scale_factor=1.2, min_neighbors=2, flags=CV_HAAR_DO_CANNY_PRUNING, 
# min_size=<minimum possible face size

min_size = (20, 20)

image_scale = 2
haar_scale = 1.2
min_neighbors = 2
haar_flags = 0

def detect_and_draw(img, cascade):

    # allocate temporary images
    gray = cv.CreateImage((img.width,img.height), 8, 1)
    small_img = cv.CreateImage((cv.Round(img.width / image_scale),
      cv.Round (img.height / image_scale)), 8, 1)

    # convert color input image to grayscale

    cv.CvtColor(img, gray, cv.CV_BGR2GRAY)

    # scale input image for faster processing

    cv.Resize(gray, small_img, cv.CV_INTER_LINEAR)

    cv.EqualizeHist(small_img, small_img)


    if(cascade):

        t = cv.GetTickCount()
        faces = cv.HaarDetectObjects(small_img, cascade, cv.CreateMemStorage(0),
                                     haar_scale, min_neighbors, haar_flags, min_size)
        t = cv.GetTickCount() - t
        print "detection time = %gms" % (t/(cv.GetTickFrequency()*1000.))
        if faces:
            for ((x, y, w, h), n) in faces:
                # the input to cv.HaarDetectObjects was resized, so scale the 
                # bounding box of each face and convert it to two CvPoints
                pt1 = (int(x * image_scale), int(y * image_scale))
                pt2 = (int((x + w) * image_scale), int((y + h) * image_scale))
                cv.Rectangle(img, pt1, pt2, cv.RGB(255, 0, 0), 3, 8, 0)

    cv.ShowImage("result", img)


if __name__ == '__main__':


    parser = OptionParser(usage = "usage: %prog [options] [filename|camera_index]")

    parser.add_option("-c", "--cascade", action="store", dest="cascade", type="str", help="Haar cascade file, default %default", default = "../data/haarcascades/haarcascade_frontalface_alt.xml")
    (options, args) = parser.parse_args()

    cascade = cv.Load(options.cascade)

    
    if len(args) != 1:
        parser.print_help()
        sys.exit(1)

    input_name = args[0]

    if input_name.isdigit():
        capture = cv.CreateCameraCapture(int(input_name))
    else:
        capture = None

    cv.NamedWindow("result", 1)


    if capture:

        frame_copy = None
        while True:
            frame = cv.QueryFrame(capture)
            if not frame:
                cv.WaitKey(0)
                break
            if not frame_copy:
                frame_copy = cv.CreateImage((frame.width,frame.height),
                                            cv.IPL_DEPTH_8U, frame.nChannels)
            if frame.origin == cv.IPL_ORIGIN_TL:
                cv.Copy(frame, frame_copy)
            else:
                cv.Flip(frame, frame_copy, 0)
            
            detect_and_draw(frame_copy, cascade)

            if cv.WaitKey(10) >= 0:

                break
    else:
        image = cv.LoadImage(input_name, 1)
        detect_and_draw(image, cascade)
        cv.WaitKey(0)

    cv.DestroyWindow("result")


.END

No comments:

Post a Comment