Skip to content

Instantly share code, notes, and snippets.

@Lukse
Last active December 17, 2019 05:39
Show Gist options
  • Save Lukse/ac5475ea574924fb7a7822dbfe483a03 to your computer and use it in GitHub Desktop.
Save Lukse/ac5475ea574924fb7a7822dbfe483a03 to your computer and use it in GitHub Desktop.
7 segment LCD Optical Character Recognition
from imutils.perspective import four_point_transform
from imutils import contours
import imutils
import cv2
import json
def rot90(img, rotflag):
""" rotFlag 1=CW, 2=CCW, 3=180"""
if rotflag == 1:
img = cv2.transpose(img)
img = cv2.flip(img, 1) # transpose+flip(1)=CW
elif rotflag == 2:
img = cv2.transpose(img)
img = cv2.flip(img, 0) # transpose+flip(0)=CCW
elif rotflag ==3:
img = cv2.flip(img, -1) # transpose+flip(-1)=180
elif rotflag != 0: # if not 0,1,2,3
raise Exception("Unknown rotation flag({})".format(rotflag))
return img
# define the dictionary of digit segments so we can identify
# each digit on the thermostat
DIGITS_LOOKUP = {
(1, 1, 1, 0, 1, 1, 1): 0,
(0, 0, 1, 0, 0, 1, 0): 1,
(1, 0, 1, 1, 1, 0, 1): 2,
(1, 0, 1, 1, 0, 1, 1): 3,
(0, 1, 1, 1, 0, 1, 0): 4,
(1, 1, 0, 1, 0, 1, 1): 5,
(1, 1, 0, 1, 1, 1, 1): 6,
(1, 0, 1, 0, 0, 1, 0): 7,
(1, 1, 1, 1, 1, 1, 1): 8,
(1, 1, 1, 1, 0, 1, 1): 9
}
# load the example image
#scale = 0.5
#image = cv2.imread("example.jpg")
image = cv2.imread("frame_masked.png")
#image = cv2.imread("08.png")
image = imutils.resize(image, width=500)
image = rot90(image, 1)
# pre-process the image by resizing it, converting it to
# graycale, blurring it, and computing an edge map
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(blurred, 50, 200, 255)
cv2.imshow('edged', edged)
# find contours in the edge map, then sort them by their
# size in descending order
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
displayCnt = None
# loop over the contours
largest = 0
for c in cnts:
# approximate the contour
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
# if the contour has four vertices, then we have found
# the thermostat display
if len(approx) == 4:
print peri
if peri > largest:
displayCnt = approx
largest = peri
#print displayCnt
cv2.drawContours(image, displayCnt, -1, (0,0,255), 5)
cv2.imshow('image00', image)
#print displayCnt.reshape(4, 2)
print displayCnt.reshape(8, 1)
#print displayCnt
data = {}
data["p1x"] = displayCnt.reshape(4, 2)[0][0]
data["p1y"] = displayCnt.reshape(4, 2)[0][1]
data["p2x"] = displayCnt.reshape(4, 2)[1][0]
data["p2y"] = displayCnt.reshape(4, 2)[1][1]
data["p3x"] = displayCnt.reshape(4, 2)[2][0]
data["p3y"] = displayCnt.reshape(4, 2)[2][1]
data["p4x"] = displayCnt.reshape(4, 2)[3][0]
data["p4y"] = displayCnt.reshape(4, 2)[3][1]
with open('lcd.json', 'w') as f:
json.dump(data, f)
warped = four_point_transform(gray, displayCnt.reshape(4, 2))
output = four_point_transform(image, displayCnt.reshape(4, 2))
cv2.imshow('warped', warped)
cv2.waitKey(0)
# import the necessary packages
from imutils.perspective import four_point_transform
from imutils import contours
import imutils
import cv2
import json
import numpy as np
def rot90(img, rotflag):
""" rotFlag 1=CW, 2=CCW, 3=180"""
if rotflag == 1:
img = cv2.transpose(img)
img = cv2.flip(img, 1) # transpose+flip(1)=CW
elif rotflag == 2:
img = cv2.transpose(img)
img = cv2.flip(img, 0) # transpose+flip(0)=CCW
elif rotflag ==3:
img = cv2.flip(img, -1) # transpose+flip(-1)=180
elif rotflag != 0: # if not 0,1,2,3
raise Exception("Unknown rotation flag({})".format(rotflag))
return img
# define the dictionary of digit segments so we can identify
# each digit on the thermostat
DIGITS_LOOKUP = {
(1, 1, 1, 0, 1, 1, 1): 0,
(0, 0, 1, 0, 0, 1, 0): 1,
(1, 0, 1, 1, 1, 0, 1): 2,
(1, 0, 1, 1, 0, 1, 1): 3,
(0, 1, 1, 1, 0, 1, 0): 4,
(1, 1, 0, 1, 0, 1, 1): 5,
(1, 1, 0, 1, 1, 1, 1): 6,
(1, 0, 1, 0, 0, 1, 0): 7,
(1, 1, 1, 1, 1, 1, 1): 8,
(1, 1, 1, 1, 0, 1, 1): 9
}
# load the example image
#scale = 0.5
#image = cv2.imread("example.jpg")
image = cv2.imread("frame.png")
#image = cv2.imread("08.png")
image = imutils.resize(image, width=500)
image = rot90(image, 1)
# pre-process the image by resizing it, converting it to
# graycale, blurring it, and computing an edge map
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(blurred, 50, 200, 255)
cv2.imshow('edged', edged)
data = None
with open('lcd.json', 'r') as f:
data = json.load(f)
#print data
displayCnt = []
displayCnt.append(data['p1x'])
displayCnt.append(data['p1y'])
displayCnt.append(data['p2x'])
displayCnt.append(data['p2y'])
displayCnt.append(data['p3x'])
displayCnt.append(data['p3y'])
displayCnt.append(data['p4x'])
displayCnt.append(data['p4y'])
displayCnt = np.asarray(displayCnt).reshape(4, 2)
#print displayCnt
warped = four_point_transform(gray, displayCnt)
output = four_point_transform(image, displayCnt)
# threshold the warped image, then apply a series of morphological
# operations to cleanup the thresholded image
#thresh = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
thresh = cv2.adaptiveThreshold(warped, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 45, 11)
#thresh = cv2.adaptiveThreshold(warped, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 45, 3)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
#kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (1, 9))
#thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
#thresh = cv2.dilate(thresh, 3)
thresh = cv2.dilate(thresh, kernel, iterations = 1)
#thresh = cv2.erode(thresh, kernel, iterations = 1)
cv2.imshow('thresh', thresh)
#cv2.waitKey(0)
# find contours in the thresholded image, then initialize the
# digit contours lists
#cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
#print cnts[0]
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
digitCnts = []
# loop over the digit area candidates
for c in cnts:
# compute the bounding box of the contour
(x, y, w, h) = cv2.boundingRect(c)
#print (x, y, w, h)
# if the contour is sufficiently large, it must be a digit
#if w >= 15 and (h >= 30 and h <= 40):
peri = cv2.contourArea(c)
if (w >= 15 and w <= 200) and (h >= 15 and h <= 250):
if peri>500 and peri<2000:
#print peri, (x, y, w, h)
digitCnts.append(c)
# sort the contours from left-to-right, then initialize the
# actual digits themselves
digitCnts = contours.sort_contours(digitCnts, method="top-to-bottom")[0]
digitCnts = contours.sort_contours(digitCnts, method="left-to-right")[0]
digits = []
cv2.drawContours(output, digitCnts, -1, (0,0,255), 1)
#cv2.imshow('output2', output)
#cv2.waitKey(0)
#print digits
# loop over each of the digits
data = {}
i = 0
for c in digitCnts:
# extract the digit ROI
(x, y, w, h) = cv2.boundingRect(c)
border = 1
cv2.rectangle(output, (x+border, y+border), (x + w-border, y + h-border), (0, 255, 0), 1)
cv2.imshow("Output", output)
cv2.waitKey(500)
roi = thresh[y:y + h, x:x + w]
#print (x, y, w, h)
number = (x+border, y+border, w-border, h-border)
data[i] = number
i += 1
with open('numbers.json', 'w') as f:
json.dump(data, f)
# display the digits
#print(u"{}{}.{} \u00b0C".format(*digits))
#print(digits)
#cv2.imshow("Input", image)
cv2.imshow("Output", output)
cv2.waitKey(0)
# import the necessary packages
from imutils.perspective import four_point_transform
from imutils import contours
import imutils
import cv2
import json
import numpy as np
def rot90(img, rotflag):
""" rotFlag 1=CW, 2=CCW, 3=180"""
if rotflag == 1:
img = cv2.transpose(img)
img = cv2.flip(img, 1) # transpose+flip(1)=CW
elif rotflag == 2:
img = cv2.transpose(img)
img = cv2.flip(img, 0) # transpose+flip(0)=CCW
elif rotflag ==3:
img = cv2.flip(img, -1) # transpose+flip(-1)=180
elif rotflag != 0: # if not 0,1,2,3
raise Exception("Unknown rotation flag({})".format(rotflag))
return img
def recognize(image):
# define the dictionary of digit segments so we can identify
# each digit on the thermostat
DIGITS_LOOKUP = {
(1, 1, 1, 0, 1, 1, 1): 0,
(0, 0, 1, 0, 0, 1, 0): 1,
(1, 0, 1, 1, 1, 0, 1): 2,
(1, 0, 1, 1, 0, 1, 1): 3,
(0, 1, 1, 1, 0, 1, 0): 4,
(1, 1, 0, 1, 0, 1, 1): 5,
(1, 1, 0, 1, 1, 1, 1): 6,
(1, 0, 1, 0, 0, 1, 0): 7,
(1, 1, 1, 1, 1, 1, 1): 8,
(1, 1, 1, 1, 0, 1, 1): 9
}
# load the example image
#scale = 0.5
#image = cv2.imread("example.jpg")
image = imutils.resize(image, width=500)
image = rot90(image, 1)
# pre-process the image by resizing it, converting it to
# graycale, blurring it, and computing an edge map
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(blurred, 50, 200, 255)
#cv2.imshow('edged', edged)
data = None
with open('lcd.json', 'r') as f:
data = json.load(f)
#print data
displayCnt = []
displayCnt.append(data['p1x'])
displayCnt.append(data['p1y'])
displayCnt.append(data['p2x'])
displayCnt.append(data['p2y'])
displayCnt.append(data['p3x'])
displayCnt.append(data['p3y'])
displayCnt.append(data['p4x'])
displayCnt.append(data['p4y'])
displayCnt = np.asarray(displayCnt).reshape(4, 2)
#print displayCnt
warped = four_point_transform(gray, displayCnt)
output = four_point_transform(image, displayCnt)
# threshold the warped image, then apply a series of morphological
# operations to cleanup the thresholded image
#thresh = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
thresh = cv2.adaptiveThreshold(warped, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 45, 11)
#thresh = cv2.adaptiveThreshold(warped, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 45, 3)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
#kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (1, 9))
#thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
#thresh = cv2.dilate(thresh, 3)
thresh = cv2.dilate(thresh, kernel, iterations = 1)
#thresh = cv2.erode(thresh, kernel, iterations = 1)
#cv2.imshow('thresh', thresh)
#cv2.waitKey(0)
data = None
with open('numbers.json', 'r') as f:
data = json.load(f)
# loop over each of the digits
digits = []
for d in xrange(len(data)):
c = data[str(d)]
#print d
(x, y, w, h) = c
roi = thresh[y:y + h, x:x + w]
#print roi
#print (x, y, w, h)
# compute the width and height of each of the 7 segments
# we are going to examine
(roiH, roiW) = roi.shape
(dW, dH) = (int(roiW * 0.25), int(roiH * 0.15))
dHC = int(roiH * 0.05)
#cv2.imshow('roi', roi)
# define the set of 7 segments
segments = [
((dW, 0), (w-dW, dH)), # top
((0, 0+4), (dW, h // 2-4)), # top-left
((w - dW-3, 0+4), (w-3, h // 2-4)), # top-right
((dW, (h // 2) - dHC*2) , (w-dW, (h // 2) + dHC*2)), # center
((0, h // 2+4), (dW, h-4)), # bottom-left
((w - dW-3, h // 2+4), (w-3, h-4)), # bottom-right
((dW, h - dH), (w-dW, h)) # bottom
]
on = [0] * len(segments)
#print on
#print segments
# loop over the segments
for (i, ((xA, yA), (xB, yB))) in enumerate(segments):
# extract the segment ROI, count the total number of
# thresholded pixels in the segment, and then compute
# the area of the segment
segROI = roi[yA:yB, xA:xB]
#print segROI
total = cv2.countNonZero(segROI)
area = (xB - xA) * (yB - yA)
#print area
# if the total number of non-zero pixels is greater than
# 50% of the area, mark the segment as "on"
if float(area) > 0:
if total / float(area) > 0.3:
on[i]= 1
cv2.rectangle(output, (x+xA, y+yA), (x+xB, y+yB), (0, 0, 200), -1)
else:
cv2.rectangle(output, (x+xA, y+yA), (x+xB, y+yB), (0, 0, 0), 1)
cv2.imshow('output3', output)
cv2.waitKey(500)
# lookup the digit and draw it on the image
try:
digit = DIGITS_LOOKUP[tuple(on)]
except:
digit = 0
#print tuple(on), digit
digits.append(digit)
#cv2.rectangle(output, (x, y), (x + w, y + h), (0, 255, 0), 1)
#cv2.putText(output, str(digit), (x + 2, y + 16), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
cv2.imshow("Output", output)
cv2.waitKey(0)
return digits
image = cv2.imread("test.png")
digits = recognize(image)
temper1 = float(digits[0]*100+digits[4]*10+digits[8]+digits[12]/10.0)
temper2 = float(digits[1]*100+digits[5]*10+digits[9]+digits[13]/10.0)
temper3 = float(digits[2]*100+digits[6]*10+digits[10]+digits[14]/10.0)
temper4 = float(digits[3]*100+digits[7]*10+digits[11]+digits[15]/10.0)
print temper1, temper2, temper3, temper4
# -*- coding: utf-8 -*-
from imutils.perspective import four_point_transform
from imutils import contours
import imutils
import cv2
import json
import numpy as np
from tqdm import trange
def rot90(img, rotflag):
""" rotFlag 1=CW, 2=CCW, 3=180"""
if rotflag == 1:
img = cv2.transpose(img)
img = cv2.flip(img, 1) # transpose+flip(1)=CW
elif rotflag == 2:
img = cv2.transpose(img)
img = cv2.flip(img, 0) # transpose+flip(0)=CCW
elif rotflag ==3:
img = cv2.flip(img, -1) # transpose+flip(-1)=180
elif rotflag != 0: # if not 0,1,2,3
raise Exception("Unknown rotation flag({})".format(rotflag))
return img
def recognize(image):
# define the dictionary of digit segments so we can identify
# each digit on the thermostat
DIGITS_LOOKUP = {
(1, 1, 1, 0, 1, 1, 1): 0,
(0, 0, 1, 0, 0, 1, 0): 1,
(1, 0, 1, 1, 1, 0, 1): 2,
(1, 0, 1, 1, 0, 1, 1): 3,
(0, 1, 1, 1, 0, 1, 0): 4,
(1, 1, 0, 1, 0, 1, 1): 5,
(1, 1, 0, 1, 1, 1, 1): 6,
(1, 0, 1, 0, 0, 1, 0): 7,
(1, 1, 1, 1, 1, 1, 1): 8,
(1, 1, 1, 1, 0, 1, 1): 9
}
# load the example image
#scale = 0.5
#image = cv2.imread("example.jpg")
image = imutils.resize(image, width=500)
image = rot90(image, 1)
# pre-process the image by resizing it, converting it to
# graycale, blurring it, and computing an edge map
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(blurred, 50, 200, 255)
#cv2.imshow('edged', edged)
data = None
with open('lcd.json', 'r') as f:
data = json.load(f)
#print data
displayCnt = []
displayCnt.append(data['p1x'])
displayCnt.append(data['p1y'])
displayCnt.append(data['p2x'])
displayCnt.append(data['p2y'])
displayCnt.append(data['p3x'])
displayCnt.append(data['p3y'])
displayCnt.append(data['p4x'])
displayCnt.append(data['p4y'])
displayCnt = np.asarray(displayCnt).reshape(4, 2)
#print displayCnt
warped = four_point_transform(gray, displayCnt)
output = four_point_transform(image, displayCnt)
# threshold the warped image, then apply a series of morphological
# operations to cleanup the thresholded image
#thresh = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
thresh = cv2.adaptiveThreshold(warped, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 45, 11)
#thresh = cv2.adaptiveThreshold(warped, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 45, 3)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
#kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (1, 9))
#thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
#thresh = cv2.dilate(thresh, 3)
thresh = cv2.dilate(thresh, kernel, iterations = 1)
#thresh = cv2.erode(thresh, kernel, iterations = 1)
#cv2.imshow('thresh', thresh)
#cv2.waitKey(0)
data = None
with open('numbers.json', 'r') as f:
data = json.load(f)
# loop over each of the digits
digits = []
for d in xrange(len(data)):
c = data[str(d)]
#print d
(x, y, w, h) = c
roi = thresh[y:y + h, x:x + w]
#print roi
#print (x, y, w, h)
# compute the width and height of each of the 7 segments
# we are going to examine
(roiH, roiW) = roi.shape
(dW, dH) = (int(roiW * 0.25), int(roiH * 0.15))
dHC = int(roiH * 0.05)
#cv2.imshow('roi', roi)
# define the set of 7 segments
segments = [
((dW, 0), (w-dW, dH)), # top
((0, 0+4), (dW, h // 2-4)), # top-left
((w - dW-3, 0+4), (w-3, h // 2-4)), # top-right
((dW, (h // 2) - dHC*2) , (w-dW, (h // 2) + dHC*2)), # center
((0, h // 2+4), (dW, h-4)), # bottom-left
((w - dW-3, h // 2+4), (w-3, h-4)), # bottom-right
((dW, h - dH), (w-dW, h)) # bottom
]
on = [0] * len(segments)
#print on
#print segments
# loop over the segments
for (i, ((xA, yA), (xB, yB))) in enumerate(segments):
# extract the segment ROI, count the total number of
# thresholded pixels in the segment, and then compute
# the area of the segment
segROI = roi[yA:yB, xA:xB]
#print segROI
total = cv2.countNonZero(segROI)
area = (xB - xA) * (yB - yA)
#print area
# if the total number of non-zero pixels is greater than
# 50% of the area, mark the segment as "on"
if float(area) > 0:
if total / float(area) > 0.3:
on[i]= 1
# cv2.rectangle(output, (x+xA, y+yA), (x+xB, y+yB), (60, 60, 60), -1)
#else:
# cv2.rectangle(output, (x+xA, y+yA), (x+xB, y+yB), (0, 0, 0), 1)
#cv2.imshow('output3', output)
#cv2.waitKey(500)
# lookup the digit and draw it on the image
try:
digit = DIGITS_LOOKUP[tuple(on)]
except:
digit = 0
#print tuple(on), digit
digits.append(digit)
cv2.rectangle(output, (x, y), (x + w, y + h), (0, 255, 0), 1)
cv2.putText(output, str(digit), (x + 2, y + 10), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 0), 1)
return digits, output
transition_frames = 10
tl1 = []
tl2 = []
tl3 = []
tl4 = []
t1 = 0
t2 = 0
t3 = 0
t4 = 0
#cap = cv2.VideoCapture('01_first_cycle.mp4')
cap = cv2.VideoCapture('02_second_cycle.mp4')
f = open("data.csv", "wb")
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
for i in trange(frame_count, unit=' frames', leave=False, dynamic_ncols=True, desc='Calculating'):
# decimate a bit, for faster processing
ret, image = cap.read()
ret, image = cap.read()
ret, image = cap.read()
ret, image = cap.read()
ret, image = cap.read()
ret, image = cap.read()
digits, output = recognize(image)
cv2.imshow("Output", output)
cv2.waitKey(1)
tl1.append(float(digits[0]*100+digits[4]*10+digits[8]+digits[12]/10.0))
tl2.append(float(digits[1]*100+digits[5]*10+digits[9]+digits[13]/10.0))
tl3.append(float(digits[2]*100+digits[6]*10+digits[10]+digits[14]/10.0))
tl4.append(float(digits[3]*100+digits[7]*10+digits[11]+digits[15]/10.0))
if len(tl1) >= transition_frames:
del tl1[0]
del tl2[0]
del tl3[0]
del tl4[0]
'''
if len(set(tl1)) == 1:
t1 = tl1[-1]
if len(set(tl2)) == 1:
t2 = tl2[-1]
if len(set(tl3)) == 1:
t3 = tl3[-1]
if len(set(tl4)) == 1:
t4 = tl4[-1]
'''
#if len(tl1) > 2:
# print tl1, tl1[-1]
if len(tl1) > 3:
if tl1[-1] == tl1[-2]:
t1 = tl1[-1]
if tl2[-1] == tl2[-2]:
t2 = tl2[-1]
if tl3[-1] == tl3[-2]:
t3 = tl3[-1]
if tl4[-1] == tl4[-2]:
t4 = tl4[-1]
string = str(t1)+', '+str(t2)+', '+str(t3)+', '+str(t4)+'\n'
f.write(string)
f.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment