-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Automatic Parking Fare Generator system
- Loading branch information
0 parents
commit 6652ea6
Showing
51 changed files
with
2,627 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
# DetectPlates.py | ||
|
||
import cv2 | ||
import numpy as np | ||
import math | ||
import license_plate | ||
import random | ||
|
||
import Preprocess | ||
import DetectChars | ||
import PossiblePlate | ||
import PossibleChar | ||
|
||
# module level variables ########################################################################## | ||
PLATE_WIDTH_PADDING_FACTOR = 1.3 | ||
PLATE_HEIGHT_PADDING_FACTOR = 1.5 | ||
|
||
################################################################################################### | ||
def detectPlatesInScene(imgOriginalScene): | ||
listOfPossiblePlates = [] # this will be the return value | ||
|
||
height, width, numChannels = imgOriginalScene.shape | ||
|
||
imgGrayscaleScene = np.zeros((height, width, 1), np.uint8) | ||
imgThreshScene = np.zeros((height, width, 1), np.uint8) | ||
imgContours = np.zeros((height, width, 3), np.uint8) | ||
|
||
cv2.destroyAllWindows() | ||
|
||
if license_plate.showSteps == True: # show steps ####################################################### | ||
cv2.imshow("0", imgOriginalScene) | ||
# end if # show steps ######################################################################### | ||
|
||
imgGrayscaleScene, imgThreshScene = Preprocess.preprocess(imgOriginalScene) # preprocess to get grayscale and threshold images | ||
|
||
if license_plate.showSteps == True: # show steps ####################################################### | ||
cv2.imshow("1a", imgGrayscaleScene) | ||
cv2.imshow("1b", imgThreshScene) | ||
# end if # show steps ######################################################################### | ||
|
||
# find all possible chars in the scene, | ||
# this function first finds all contours, then only includes contours that could be chars (without comparison to other chars yet) | ||
listOfPossibleCharsInScene = findPossibleCharsInScene(imgThreshScene) | ||
|
||
if license_plate.showSteps == True: # show steps ####################################################### | ||
print( "step 2 - len(listOfPossibleCharsInScene) = " + str(len(listOfPossibleCharsInScene))) # 131 with MCLRNF1 image | ||
|
||
imgContours = np.zeros((height, width, 3), np.uint8) | ||
|
||
contours = [] | ||
|
||
for possibleChar in listOfPossibleCharsInScene: | ||
contours.append(possibleChar.contour) | ||
# end for | ||
|
||
cv2.drawContours(imgContours, contours, -1, license_plate.SCALAR_WHITE) | ||
cv2.imshow("2b", imgContours) | ||
# end if # show steps ######################################################################### | ||
|
||
# given a list of all possible chars, find groups of matching chars | ||
# in the next steps each group of matching chars will attempt to be recognized as a plate | ||
listOfListsOfMatchingCharsInScene = DetectChars.findListOfListsOfMatchingChars(listOfPossibleCharsInScene) | ||
|
||
if license_plate.showSteps == True: # show steps ####################################################### | ||
print ("step 3 - listOfListsOfMatchingCharsInScene.Count = " + str(len(listOfListsOfMatchingCharsInScene))) # 13 with MCLRNF1 image | ||
|
||
imgContours = np.zeros((height, width, 3), np.uint8) | ||
|
||
for listOfMatchingChars in listOfListsOfMatchingCharsInScene: | ||
intRandomBlue = random.randint(0, 255) | ||
intRandomGreen = random.randint(0, 255) | ||
intRandomRed = random.randint(0, 255) | ||
|
||
contours = [] | ||
|
||
for matchingChar in listOfMatchingChars: | ||
contours.append(matchingChar.contour) | ||
# end for | ||
|
||
cv2.drawContours(imgContours, contours, -1, (intRandomBlue, intRandomGreen, intRandomRed)) | ||
# end for | ||
|
||
cv2.imshow("3", imgContours) | ||
# end if # show steps ######################################################################### | ||
|
||
for listOfMatchingChars in listOfListsOfMatchingCharsInScene: # for each group of matching chars | ||
possiblePlate = extractPlate(imgOriginalScene, listOfMatchingChars) # attempt to extract plate | ||
|
||
if possiblePlate.imgPlate is not None: # if plate was found | ||
listOfPossiblePlates.append(possiblePlate) # add to list of possible plates | ||
# end if | ||
# end for | ||
|
||
'''print ("\n" + str(len(listOfPossiblePlates)) + " possible plates found")''' | ||
print ("Number Plate Detected") | ||
# 13 with MCLRNF1 image | ||
|
||
if license_plate.showSteps == True: # show steps ####################################################### | ||
print ("\n") | ||
cv2.imshow("4a", imgContours) | ||
|
||
for i in range(0, len(listOfPossiblePlates)): | ||
p2fRectPoints = cv2.boxPoints(listOfPossiblePlates[i].rrLocationOfPlateInScene) | ||
|
||
cv2.line(imgContours, tuple(p2fRectPoints[0]), tuple(p2fRectPoints[1]), license_plate.SCALAR_RED, 2) | ||
cv2.line(imgContours, tuple(p2fRectPoints[1]), tuple(p2fRectPoints[2]), license_plate.SCALAR_RED, 2) | ||
cv2.line(imgContours, tuple(p2fRectPoints[2]), tuple(p2fRectPoints[3]), license_plate.SCALAR_RED, 2) | ||
cv2.line(imgContours, tuple(p2fRectPoints[3]), tuple(p2fRectPoints[0]), license_plate.SCALAR_RED, 2) | ||
|
||
cv2.imshow("4a", imgContours) | ||
|
||
print ("possible plate " + str(i) + ", click on any image and press a key to continue . . .") | ||
|
||
cv2.imshow("4b", listOfPossiblePlates[i].imgPlate) | ||
cv2.waitKey(0) | ||
# end for | ||
|
||
print ("\nplate detection complete, click on any image and press a key to begin char recognition . . .\n") | ||
cv2.waitKey(0) | ||
# end if # show steps ######################################################################### | ||
|
||
return listOfPossiblePlates | ||
# end function | ||
|
||
################################################################################################### | ||
def findPossibleCharsInScene(imgThresh): | ||
listOfPossibleChars = [] # this will be the return value | ||
|
||
intCountOfPossibleChars = 0 | ||
|
||
imgThreshCopy = imgThresh.copy() | ||
|
||
imgContours, contours, npaHierarchy = cv2.findContours(imgThreshCopy, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) # find all contours | ||
|
||
height, width = imgThresh.shape | ||
imgContours = np.zeros((height, width, 3), np.uint8) | ||
|
||
for i in range(0, len(contours)): # for each contour | ||
|
||
if license_plate.showSteps == True: # show steps ################################################### | ||
cv2.drawContours(imgContours, contours, i, license_plate.SCALAR_WHITE) | ||
# end if # show steps ##################################################################### | ||
|
||
possibleChar = PossibleChar.PossibleChar(contours[i]) | ||
|
||
if DetectChars.checkIfPossibleChar(possibleChar): # if contour is a possible char, note this does not compare to other chars (yet) . . . | ||
intCountOfPossibleChars = intCountOfPossibleChars + 1 # increment count of possible chars | ||
listOfPossibleChars.append(possibleChar) # and add to list of possible chars | ||
# end if | ||
# end for | ||
|
||
if license_plate.showSteps == True: # show steps ####################################################### | ||
print ("\nstep 2 - len(contours) = " + str(len(contours))) # 2362 with MCLRNF1 image | ||
print ("step 2 - intCountOfPossibleChars = " + str(intCountOfPossibleChars)) # 131 with MCLRNF1 image | ||
cv2.imshow("2a", imgContours) | ||
# end if # show steps ######################################################################### | ||
|
||
return listOfPossibleChars | ||
# end function | ||
|
||
|
||
################################################################################################### | ||
def extractPlate(imgOriginal, listOfMatchingChars): | ||
possiblePlate = PossiblePlate.PossiblePlate() # this will be the return value | ||
|
||
listOfMatchingChars.sort(key = lambda matchingChar: matchingChar.intCenterX) # sort chars from left to right based on x position | ||
|
||
# calculate the center point of the plate | ||
fltPlateCenterX = (listOfMatchingChars[0].intCenterX + listOfMatchingChars[len(listOfMatchingChars) - 1].intCenterX) / 2.0 | ||
fltPlateCenterY = (listOfMatchingChars[0].intCenterY + listOfMatchingChars[len(listOfMatchingChars) - 1].intCenterY) / 2.0 | ||
|
||
ptPlateCenter = fltPlateCenterX, fltPlateCenterY | ||
|
||
# calculate plate width and height | ||
intPlateWidth = int((listOfMatchingChars[len(listOfMatchingChars) - 1].intBoundingRectX + listOfMatchingChars[len(listOfMatchingChars) - 1].intBoundingRectWidth - listOfMatchingChars[0].intBoundingRectX) * PLATE_WIDTH_PADDING_FACTOR) | ||
|
||
intTotalOfCharHeights = 0 | ||
|
||
for matchingChar in listOfMatchingChars: | ||
intTotalOfCharHeights = intTotalOfCharHeights + matchingChar.intBoundingRectHeight | ||
# end for | ||
|
||
fltAverageCharHeight = intTotalOfCharHeights / len(listOfMatchingChars) | ||
|
||
intPlateHeight = int(fltAverageCharHeight * PLATE_HEIGHT_PADDING_FACTOR) | ||
|
||
# calculate correction angle of plate region | ||
fltOpposite = listOfMatchingChars[len(listOfMatchingChars) - 1].intCenterY - listOfMatchingChars[0].intCenterY | ||
fltHypotenuse = DetectChars.distanceBetweenChars(listOfMatchingChars[0], listOfMatchingChars[len(listOfMatchingChars) - 1]) | ||
fltCorrectionAngleInRad = math.asin(fltOpposite / fltHypotenuse) | ||
fltCorrectionAngleInDeg = fltCorrectionAngleInRad * (180.0 / math.pi) | ||
|
||
# pack plate region center point, width and height, and correction angle into rotated rect member variable of plate | ||
possiblePlate.rrLocationOfPlateInScene = ( tuple(ptPlateCenter), (intPlateWidth, intPlateHeight), fltCorrectionAngleInDeg ) | ||
|
||
# final steps are to perform the actual rotation | ||
|
||
# get the rotation matrix for our calculated correction angle | ||
rotationMatrix = cv2.getRotationMatrix2D(tuple(ptPlateCenter), fltCorrectionAngleInDeg, 1.0) | ||
|
||
height, width, numChannels = imgOriginal.shape # unpack original image width and height | ||
|
||
imgRotated = cv2.warpAffine(imgOriginal, rotationMatrix, (width, height)) # rotate the entire image | ||
|
||
imgCropped = cv2.getRectSubPix(imgRotated, (intPlateWidth, intPlateHeight), tuple(ptPlateCenter)) | ||
|
||
possiblePlate.imgPlate = imgCropped # copy the cropped plate image into the applicable member variable of the possible plate | ||
|
||
return possiblePlate | ||
# end function | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
License_Plate_Number,Vehicle Type,Entry Time | ||
IC8S,Bike,1525341533.931944 | ||
H14FE5621,Bike,1525342067.0251193 | ||
III,Car,1525343747.0427272 | ||
MCLRNF1,Car,1525415658.5371869 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# PossibleChar.py | ||
|
||
import cv2 | ||
import numpy as np | ||
import math | ||
|
||
################################################################################################### | ||
class PossibleChar: | ||
|
||
# constructor ################################################################################# | ||
def __init__(self, _contour): | ||
self.contour = _contour | ||
|
||
self.boundingRect = cv2.boundingRect(self.contour) | ||
|
||
[intX, intY, intWidth, intHeight] = self.boundingRect | ||
|
||
self.intBoundingRectX = intX | ||
self.intBoundingRectY = intY | ||
self.intBoundingRectWidth = intWidth | ||
self.intBoundingRectHeight = intHeight | ||
|
||
self.intBoundingRectArea = self.intBoundingRectWidth * self.intBoundingRectHeight | ||
|
||
self.intCenterX = (self.intBoundingRectX + self.intBoundingRectX + self.intBoundingRectWidth) / 2 | ||
self.intCenterY = (self.intBoundingRectY + self.intBoundingRectY + self.intBoundingRectHeight) / 2 | ||
|
||
self.fltDiagonalSize = math.sqrt((self.intBoundingRectWidth ** 2) + (self.intBoundingRectHeight ** 2)) | ||
|
||
self.fltAspectRatio = float(self.intBoundingRectWidth) / float(self.intBoundingRectHeight) | ||
# end constructor | ||
|
||
# end class | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# PossiblePlate.py | ||
|
||
import cv2 | ||
import numpy as np | ||
|
||
################################################################################################### | ||
class PossiblePlate: | ||
|
||
# constructor ################################################################################# | ||
def __init__(self): | ||
self.imgPlate = None | ||
self.imgGrayscale = None | ||
self.imgThresh = None | ||
|
||
self.rrLocationOfPlateInScene = None | ||
|
||
self.strChars = "" | ||
# end constructor | ||
|
||
# end class | ||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# Preprocess.py | ||
|
||
import cv2 | ||
import numpy as np | ||
import math | ||
|
||
# module level variables ########################################################################## | ||
GAUSSIAN_SMOOTH_FILTER_SIZE = (5, 5) | ||
ADAPTIVE_THRESH_BLOCK_SIZE = 19 | ||
ADAPTIVE_THRESH_WEIGHT = 9 | ||
|
||
################################################################################################### | ||
def preprocess(imgOriginal): | ||
imgGrayscale = extractValue(imgOriginal) | ||
|
||
imgMaxContrastGrayscale = maximizeContrast(imgGrayscale) | ||
|
||
height, width = imgGrayscale.shape | ||
|
||
imgBlurred = np.zeros((height, width, 1), np.uint8) | ||
|
||
imgBlurred = cv2.GaussianBlur(imgMaxContrastGrayscale, GAUSSIAN_SMOOTH_FILTER_SIZE, 0) | ||
|
||
imgThresh = cv2.adaptiveThreshold(imgBlurred, 255.0, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, ADAPTIVE_THRESH_BLOCK_SIZE, ADAPTIVE_THRESH_WEIGHT) | ||
|
||
return imgGrayscale, imgThresh | ||
# end function | ||
|
||
################################################################################################### | ||
def extractValue(imgOriginal): | ||
height, width, numChannels = imgOriginal.shape | ||
|
||
imgHSV = np.zeros((height, width, 3), np.uint8) | ||
|
||
imgHSV = cv2.cvtColor(imgOriginal, cv2.COLOR_BGR2HSV) | ||
|
||
imgHue, imgSaturation, imgValue = cv2.split(imgHSV) | ||
|
||
return imgValue | ||
# end function | ||
|
||
################################################################################################### | ||
def maximizeContrast(imgGrayscale): | ||
|
||
height, width = imgGrayscale.shape | ||
|
||
imgTopHat = np.zeros((height, width, 1), np.uint8) | ||
imgBlackHat = np.zeros((height, width, 1), np.uint8) | ||
|
||
structuringElement = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) | ||
|
||
imgTopHat = cv2.morphologyEx(imgGrayscale, cv2.MORPH_TOPHAT, structuringElement) | ||
imgBlackHat = cv2.morphologyEx(imgGrayscale, cv2.MORPH_BLACKHAT, structuringElement) | ||
|
||
imgGrayscalePlusTopHat = cv2.add(imgGrayscale, imgTopHat) | ||
imgGrayscalePlusTopHatMinusBlackHat = cv2.subtract(imgGrayscalePlusTopHat, imgBlackHat) | ||
|
||
return imgGrayscalePlusTopHatMinusBlackHat | ||
# end function | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Oops, something went wrong.