Search code examples
pythonopencvface-detection

ambiguous results in face and eye detection when image is resized


I am trying out a code for face and eye detection in Open CV using Python. The code works well for image size 2848 X 4272 and even when I resized it by a factor of 0.5. But whenever I am resizing it with another factors such as 0.2,0.4 etc , it gives me ambiguous results for eyes(such as few regions of forehead, nose).In that case I am not able to get a generalised code for all image sizes. Is there any code so that I get correct detections with any image size as its very difficult to process such big images. The code is as such

import numpy as np
import cv2
import cv2.cv as cv

#attaching the haar cascade files
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')

# reading the image
img11 = cv2.imread('IMG_0347.JPG')

if img11 !=None:

# resizing the image
    w,h,c= img11.shape
    print "dimension"
    print w,h
    img = cv2.resize(img11,None,fx=0.4, fy=0.3, interpolation =   cv2.INTER_LINEAR)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # converting into grayscale
    gray = cv2.equalizeHist(gray)
    #cv2.imshow('histo',gray)
    w,h,c= img.shape # finding out the dimensions of the image i.e width, height and number of channels

# creating a white background of same dimensions as input image for pasting the eyes detected by 'haarcascade_eye.xml'
    im = np.zeros((w,h,c),np.uint8)
    im[:]=[255,255,255]

# creating a white background of same dimensions as input image for pasting the masked eyes
    im_mask = np.zeros((w,h,c),np.uint8)
    im_mask[:]=[255,255,255]

# faces gives the top left coordinates of the detected face and width and height of the rectangle
    faces = face_cascade.detectMultiScale(gray, 1.5, 5)

# taking face as the ROI
    for (x,y,w,h) in faces:
                cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),1) # Draws the rectangle around the detected face
                roi_gray = gray[y:y+h, x:x+w]
                roi_color = img[y:y+h, x:x+w]
                #cv2.imshow('image1',img)  # shows the original image with face detected
                #cv2.imshow('image1',roi_color)  # shows only the face detected (colored)

# searching for eyes in the detected face i.e in the roi gray
                eyes = eye_cascade.detectMultiScale(roi_gray)
#print eyes # prints the top left coordinates of the detected eyes and width and height of the rectangle
                if eyes.any():
                    for (ex,ey,ew,eh)in eyes:
                        cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),1) # draws rectangle around the masked eyes
                        eye_mask= roi_color[ey+1:u, ex+1:ex+ew]                             # eye_mask is the masked portion of the detected eye extracted from roi_color
                        im_mask[ey+1+y:y+u, ex+x+1:ex+ew+x]=eye_mask         #pasting the eye_mask on the white background called im_mask
                else:
                        print ("eyes could not be detected")

            cv2.imshow('image',im_mask)   #shows the im-mask white background with masked eyes pasted on it

Solution

  • It is logical that as the image gets smaller and smaller, it becomes harder to differentiate an eye from a nose, for example. So unless you understand fundamentally what your image analysis functions are looking for (I don't) it's hard to know the best way to downsize your images while retaining the type of information that the analysis needs.

    Having said that, I believe cv2.INTER_AREA is used for shrinking images more commonly than cv2.INTER_LINEAR etc.

    Try this instead of the resize you have:

    img = cv2.resize(img11, None, fx=0.4, fy=0.3, interpolation=cv2.INTER_AREA)
    

    Also, aren't you making it harder to identify eyes by changing the aspect ratio of your images (fx != fy)? If you don't have a special reason for that, you can just choose the target size explicitly with the second position argument, size. For example:

    effective_but_smaller_size = (640, 480)  # or whatever you find works
    img = cv2.resize(img11, effective_but_smaller_size, interpolation=cv2.INTER_AREA)