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()
@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