Skip to content

Instantly share code, notes, and snippets.

@edfungus
Last active November 27, 2023 17:08
Show Gist options
  • Save edfungus/67c14af0d5afaae5b18c to your computer and use it in GitHub Desktop.
Save edfungus/67c14af0d5afaae5b18c to your computer and use it in GitHub Desktop.
Pupil Detection with Python and OpenCV
#Identify pupils. Based on beta 1
import numpy as np
import cv2
import time
cap = cv2.VideoCapture(0) #640,480
w = 640
h = 480
while(cap.isOpened()):
ret, frame = cap.read()
if ret==True:
#downsample
#frameD = cv2.pyrDown(cv2.pyrDown(frame))
#frameDBW = cv2.cvtColor(frameD,cv2.COLOR_RGB2GRAY)
#detect face
frame = cv2.cvtColor(frame,cv2.COLOR_RGB2GRAY)
faces = cv2.CascadeClassifier('haarcascade_eye.xml')
detected = faces.detectMultiScale(frame, 1.3, 5)
#faces = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
#detected2 = faces.detectMultiScale(frameDBW, 1.3, 5)
pupilFrame = frame
pupilO = frame
windowClose = np.ones((5,5),np.uint8)
windowOpen = np.ones((2,2),np.uint8)
windowErode = np.ones((2,2),np.uint8)
#draw square
for (x,y,w,h) in detected:
cv2.rectangle(frame, (x,y), ((x+w),(y+h)), (0,0,255),1)
cv2.line(frame, (x,y), ((x+w,y+h)), (0,0,255),1)
cv2.line(frame, (x+w,y), ((x,y+h)), (0,0,255),1)
pupilFrame = cv2.equalizeHist(frame[y+(h*.25):(y+h), x:(x+w)])
pupilO = pupilFrame
ret, pupilFrame = cv2.threshold(pupilFrame,55,255,cv2.THRESH_BINARY) #50 ..nothin 70 is better
pupilFrame = cv2.morphologyEx(pupilFrame, cv2.MORPH_CLOSE, windowClose)
pupilFrame = cv2.morphologyEx(pupilFrame, cv2.MORPH_ERODE, windowErode)
pupilFrame = cv2.morphologyEx(pupilFrame, cv2.MORPH_OPEN, windowOpen)
#so above we do image processing to get the pupil..
#now we find the biggest blob and get the centriod
threshold = cv2.inRange(pupilFrame,250,255) #get the blobs
contours, hierarchy = cv2.findContours(threshold,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
#if there are 3 or more blobs, delete the biggest and delete the left most for the right eye
#if there are 2 blob, take the second largest
#if there are 1 or less blobs, do nothing
if len(contours) >= 2:
#find biggest blob
maxArea = 0
MAindex = 0 #to get the unwanted frame
distanceX = [] #delete the left most (for right eye)
currentIndex = 0
for cnt in contours:
area = cv2.contourArea(cnt)
center = cv2.moments(cnt)
cx,cy = int(center['m10']/center['m00']), int(center['m01']/center['m00'])
distanceX.append(cx)
if area > maxArea:
maxArea = area
MAindex = currentIndex
currentIndex = currentIndex + 1
del contours[MAindex] #remove the picture frame contour
del distanceX[MAindex]
eye = 'right'
if len(contours) >= 2: #delete the left most blob for right eye
if eye == 'right':
edgeOfEye = distanceX.index(min(distanceX))
else:
edgeOfEye = distanceX.index(max(distanceX))
del contours[edgeOfEye]
del distanceX[edgeOfEye]
if len(contours) >= 1: #get largest blob
maxArea = 0
for cnt in contours:
area = cv2.contourArea(cnt)
if area > maxArea:
maxArea = area
largeBlob = cnt
if len(largeBlob) > 0:
center = cv2.moments(largeBlob)
cx,cy = int(center['m10']/center['m00']), int(center['m01']/center['m00'])
cv2.circle(pupilO,(cx,cy),5,255,-1)
#show picture
cv2.imshow('frame',pupilO)
cv2.imshow('frame2',pupilFrame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
#else:
#break
# Release everything if job is finished
cap.release()
cv2.destroyAllWindows()
@naushadck
Copy link

To -->- bma131 commented on Mar 21

Your codes did not work for me. It was giving the following error. 

image

After changing the following line, it worked...... (removed "_,")
_,contours, hierarchy = cv2.findContours(threshold,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)

 changed to 

contours, hierarchy = cv2.findContours(threshold,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)

However the issue with blank paper or id cards(with or without picture) shows the pupil on the screen.

@artun3e
Copy link

artun3e commented Sep 8, 2020

It worked for me correctly thanks for the effort!
I have one question though, what does maxArea contains in each iteration ? Blob size for right eye ?

@naushadck
Copy link

  1. It worked for me correctly thanks for the effort!
    Which one worked for you ?.
    The program itself or the spoofing(id card, blank paper etc......)

  2. Are you referring this maxArea = 0
    I think it is sort of flag to find the biggest area.
    Please see the following line in the code.
    if area > maxArea:
    maxArea = area
    From the above code, area moving to maxarea, if it is bigger than the previous. This way can find the biggest one.

@artun3e
Copy link

artun3e commented Sep 23, 2020

  1. It worked for me correctly thanks for the effort!
    Which one worked for you ?. -- pupil.py file itself worked for me!
    The program itself or the spoofing(id card, blank paper etc......) -- the program itself in this case
  2. Are you referring this maxArea = 0
    I think it is sort of flag to find the biggest area.
    Please see the following line in the code.
    if area > maxArea:
    maxArea = area
    From the above code, area moving to maxarea, if it is bigger than the previous. This way can find the biggest one.

What I wonder here is, sorry for the late answer I've been offline due to self-isolation and I had no access to my laptop anyways. What does maxArea corresponds in here ? As far as I understood, it is the area of the biggest contour in eye and it actually indicates the pupil. But I'm not sure about it.

@vuong2102
Copy link

vuong2102 commented Sep 27, 2023

hi friends, thank for your source code. But I have a trouble like
", line 70, in
del contours[MAindex] # remove the picture frame contour
~~~~~~~~^^^^^^^^^
TypeError: 'tuple' object doesn't support item deletion

if I set cap is a image in 5th line, it's work. But not working with videocapture.
Please, help me if you can.

@warpdriv
Copy link

With the help of github copilot I am working through the issues. My setup is win 11 py 3.12.0. I have added comment with my name. I hopes this helps others.

I'm at troubleshooting this error
File "D:\py\pupil\measure.py", line 100, in
del contours[MAindex]
~~~~~~~~^^^^^^^^^
IndexError: list assignment index out of range

#Identify pupils. Based on beta 1

import numpy as np
import cv2
import time

cap = cv2.VideoCapture(0) #640,480
w = 640
h = 480

while(cap.isOpened()):
ret, frame = cap.read()
if ret==True:

	#downsample
	#frameD = cv2.pyrDown(cv2.pyrDown(frame))
	#frameDBW = cv2.cvtColor(frameD,cv2.COLOR_RGB2GRAY)

	#detect face
	frame = cv2.cvtColor(frame,cv2.COLOR_RGB2GRAY)
	#warprdiv added to resolve file location issue
	#faces = cv2.CascadeClassifier('haarcascade_eye.xml')
	faces = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')
	
	detected = faces.detectMultiScale(frame, 1.3, 5)

	#faces = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
	#detected2 = faces.detectMultiScale(frameDBW, 1.3, 5)
	
	pupilFrame = frame
	pupilO = frame
	windowClose = np.ones((5,5),np.uint8)
	windowOpen = np.ones((2,2),np.uint8)
	windowErode = np.ones((2,2),np.uint8)

	#draw square
	for (x,y,w,h) in detected:
		cv2.rectangle(frame, (x,y), ((x+w),(y+h)), (0,0,255),1)	
		cv2.line(frame, (x,y), ((x+w,y+h)), (0,0,255),1)
		cv2.line(frame, (x+w,y), ((x,y+h)), (0,0,255),1)
		#pupilFrame = cv2.equalizeHist(frame[y+(h*.25):(y+h), x:(x+w)])
		pupilFrame = cv2.equalizeHist(frame[int(y+(h*.25)):int(y+h), int(x):int(x+w)])
		pupilO = pupilFrame
		ret, pupilFrame = cv2.threshold(pupilFrame,55,255,cv2.THRESH_BINARY)		#50 ..nothin 70 is better
		pupilFrame = cv2.morphologyEx(pupilFrame, cv2.MORPH_CLOSE, windowClose)
		pupilFrame = cv2.morphologyEx(pupilFrame, cv2.MORPH_ERODE, windowErode)
		pupilFrame = cv2.morphologyEx(pupilFrame, cv2.MORPH_OPEN, windowOpen)

		#so above we do image processing to get the pupil..
		#now we find the biggest blob and get the centriod
		
		threshold = cv2.inRange(pupilFrame,250,255)		#get the blobs
		contours, hierarchy = cv2.findContours(threshold,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
		
		#if there are 3 or more blobs, delete the biggest and delete the left most for the right eye
		#if there are 2 blob, take the second largest
		#if there are 1 or less blobs, do nothing
		
		if len(contours) >= 2:
			#find biggest blob
			maxArea = 0
			MAindex = 0			#to get the unwanted frame 
			distanceX = []		#delete the left most (for right eye)
			currentIndex = 0 
			for cnt in contours:
				area = cv2.contourArea(cnt)
				center = cv2.moments(cnt)
				#warpdriv added take care of divide by zero error
				if center['m00'] != 0:	
					cx,cy = int(center['m10']/center['m00']), int(center['m01']/center['m00'])
				distanceX.append(cx)	
				if area > maxArea:
					maxArea = area
					MAindex = currentIndex
				currentIndex = currentIndex + 1
	
			#del contours[MAindex]		#remove the picture frame contour
			#warpdriv added
			contours = list(contours)
			del contours[MAindex]
			contours = tuple(contours)

			#del distanceX[MAindex]
			#warpdriv added
			distanceX = list(distanceX)
			del distanceX[MAindex]
			distanceX = tuple(distanceX)
		
		eye = 'right'

		if len(contours) >= 2:		#delete the left most blob for right eye
			if eye == 'right':
				edgeOfEye = distanceX.index(min(distanceX))
			else:
				edgeOfEye = distanceX.index(max(distanceX))	

			#warpdriv added
			#del contours[MAindex]		#remove the picture frame contour
			contours = list(contours)
			del contours[MAindex]
			contours = tuple(contours)

			#warpdriv added
			#del distanceX[MAindex]
			distanceX = list(distanceX)
			del distanceX[MAindex]
			distanceX = tuple(distanceX)

		if len(contours) >= 1:		#get largest blob
			maxArea = 0
			for cnt in contours:
				area = cv2.contourArea(cnt)
				if area > maxArea:
					maxArea = area
					largeBlob = cnt
				
		if len(largeBlob) > 0:	
			center = cv2.moments(largeBlob)
			cx,cy = int(center['m10']/center['m00']), int(center['m01']/center['m00'])
			cv2.circle(pupilO,(cx,cy),5,255,-1)


	#show picture
	cv2.imshow('frame',pupilO)
	cv2.imshow('frame2',pupilFrame)
	if cv2.waitKey(1) & 0xFF == ord('q'):
		break

#else:
	#break

Release everything if job is finished

cap.release()
cv2.destroyAllWindows()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment