Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
5 changed files
with
529 additions
and
0 deletions.
There are no files selected for viewing
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,243 @@ | ||
import os | ||
|
||
import cv2 | ||
import numpy as np | ||
import math | ||
import random | ||
|
||
import Main | ||
import Preprocess | ||
import PossibleChar | ||
|
||
kNearest = cv2.ml.KNearest_create() | ||
|
||
m_p_w = 2 # min pixel width | ||
m_p_h = 8 #min pixel height | ||
|
||
min_a_r = 0.25 # min aspect ratio | ||
max_a_r = 1.0 # max aspect ratio | ||
|
||
m_p_a = 80 # min pixel area | ||
|
||
# min and max diag size multiple away | ||
min_d_s_m_a = 0.3 | ||
max_d_s_m_a = 5.0 | ||
|
||
max_c_i_a = 0.5 | ||
|
||
max_c_i_w = 0.8 | ||
max_c_i_h = 0.2 | ||
|
||
max_a_b_c = 12.0 | ||
|
||
min_n_o_m_c = 3 | ||
|
||
r_c_i_w = 20 | ||
r_c_i_h = 30 | ||
|
||
MIN_CONTOUR_AREA = 100 | ||
|
||
def findPossibleCharsInPlate(imgGrayscale, imgThresh): | ||
PossCharsList = [] | ||
contours = [] | ||
imgThreshCopy = imgThresh.copy() | ||
|
||
contours, npaHierarchy = cv2.findContours(imgThreshCopy, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) | ||
|
||
for contour in contours: | ||
possibleChar = PossibleChar.PossibleChar(contour) | ||
|
||
if checkIfPossibleChar(possibleChar): | ||
PossCharsList.append(possibleChar) | ||
|
||
return PossCharsList | ||
|
||
def checkIfPossibleChar(possibleChar): | ||
|
||
if (possibleChar.intBoundingRectArea > m_p_a and | ||
possibleChar.intBoundingRectWidth > m_p_w and possibleChar.intBoundingRectHeight > m_p_h and | ||
min_a_r < possibleChar.fltAspectRatio and possibleChar.fltAspectRatio < max_a_r): | ||
return True | ||
else: | ||
return False | ||
|
||
def findrecursLOMatchChars(PossCharsList): | ||
|
||
recursLOMatchChars = [] | ||
|
||
for possibleChar in PossCharsList: | ||
listOfMatchingChars = findListOfMatchingChars(possibleChar, PossCharsList) | ||
|
||
listOfMatchingChars.append(possibleChar) | ||
|
||
if len(listOfMatchingChars) < min_n_o_m_c: | ||
continue | ||
|
||
recursLOMatchChars.append(listOfMatchingChars) | ||
|
||
PossCharsListWithCurrentMatchesRemoved = [] | ||
PossCharsListWithCurrentMatchesRemoved = list(set(PossCharsList) - set(listOfMatchingChars)) | ||
|
||
recursiveListOfMatchingChars = findrecursLOMatchChars(PossCharsListWithCurrentMatchesRemoved) | ||
|
||
for recursiveListOfMatchingChars in recursiveListOfMatchingChars: | ||
recursLOMatchChars.append(recursiveListOfMatchingChars) | ||
break | ||
|
||
return recursLOMatchChars | ||
|
||
def detectCharsInPlates(listOfpossPlates): | ||
intPlateCounter = 0 | ||
imgContours = None | ||
contours = [] | ||
|
||
if len(listOfpossPlates) == 0: | ||
return listOfpossPlates | ||
|
||
|
||
|
||
|
||
for possPlate in listOfpossPlates: | ||
|
||
possPlate.imgGrayscale, possPlate.imgThresh = Preprocess.preprocess(possPlate.imgPlate) | ||
possPlate.imgThresh = cv2.resize(possPlate.imgThresh, (0, 0), fx = 1.6, fy = 1.6) | ||
thresholdValue, possPlate.imgThresh = cv2.threshold(possPlate.imgThresh, 0.0, 255.0, cv2.THRESH_BINARY | cv2.THRESH_OTSU) | ||
PossCharsListInPlate = findPossibleCharsInPlate(possPlate.imgGrayscale, possPlate.imgThresh) | ||
recursLOMatchCharsInPlate = findrecursLOMatchChars(PossCharsListInPlate) | ||
|
||
if (len(recursLOMatchCharsInPlate) == 0): | ||
possPlate.strChars = "" | ||
continue | ||
|
||
for i in range(0, len(recursLOMatchCharsInPlate)): | ||
recursLOMatchCharsInPlate[i].sort(key = lambda matchingChar: matchingChar.intCenterX) | ||
recursLOMatchCharsInPlate[i] = removeInnerOverlappingChars(recursLOMatchCharsInPlate[i]) | ||
intLenOfLongestListOfChars = 0 | ||
intIndexOfLongestListOfChars = 0 | ||
for i in range(0, len(recursLOMatchCharsInPlate)): | ||
if len(recursLOMatchCharsInPlate[i]) > intLenOfLongestListOfChars: | ||
intLenOfLongestListOfChars = len(recursLOMatchCharsInPlate[i]) | ||
intIndexOfLongestListOfChars = i | ||
|
||
longestListOfMatchingCharsInPlate = recursLOMatchCharsInPlate[intIndexOfLongestListOfChars] | ||
|
||
possPlate.strChars = recognizeCharsInPlate(possPlate.imgThresh, longestListOfMatchingCharsInPlate) | ||
|
||
return listOfpossPlates | ||
|
||
def loadKNNDataAndTrainKNN(): | ||
try: | ||
npaClass = np.loadtxt("classifications.txt", np.float32) | ||
except: | ||
print("error, cant open classifications.txt, exiting program\n") | ||
os.system("pause") | ||
return False | ||
|
||
try: | ||
npaFlattenedImages = np.loadtxt("flattened_images.txt", np.float32) | ||
except: | ||
print("error, cant open flattened_images.txt, exiting program\n") | ||
os.system("pause") | ||
return False | ||
|
||
npaClass = npaClass.reshape((npaClass.size, 1)) | ||
kNearest.setDefaultK(1) | ||
kNearest.train(npaFlattenedImages, cv2.ml.ROW_SAMPLE, npaClass) | ||
|
||
return True | ||
|
||
|
||
|
||
def findListOfMatchingChars(possibleChar, listOfChars): | ||
|
||
listOfMatchingChars = [] | ||
|
||
for possibleMatchingChar in listOfChars: | ||
if possibleMatchingChar == possibleChar: | ||
continue | ||
|
||
fltDistanceBetweenChars = distanceBetweenChars(possibleChar, possibleMatchingChar) | ||
|
||
fltAngleBetweenChars = angleBetweenChars(possibleChar, possibleMatchingChar) | ||
|
||
fltChangeInArea = float(abs(possibleMatchingChar.intBoundingRectArea - possibleChar.intBoundingRectArea)) / float(possibleChar.intBoundingRectArea) | ||
|
||
fltChangeInWidth = float(abs(possibleMatchingChar.intBoundingRectWidth - possibleChar.intBoundingRectWidth)) / float(possibleChar.intBoundingRectWidth) | ||
fltChangeInHeight = float(abs(possibleMatchingChar.intBoundingRectHeight - possibleChar.intBoundingRectHeight)) / float(possibleChar.intBoundingRectHeight) | ||
|
||
if (fltDistanceBetweenChars < (possibleChar.fltDiagonalSize * max_d_s_m_a) and | ||
fltAngleBetweenChars < max_a_b_c and | ||
fltChangeInArea < max_c_i_a and | ||
fltChangeInWidth < max_c_i_w and | ||
fltChangeInHeight < max_c_i_h): | ||
|
||
listOfMatchingChars.append(possibleMatchingChar) | ||
return listOfMatchingChars | ||
|
||
def distanceBetweenChars(firstChar, secondChar): | ||
intX = abs(firstChar.intCenterX - secondChar.intCenterX) | ||
intY = abs(firstChar.intCenterY - secondChar.intCenterY) | ||
|
||
return math.sqrt((intX ** 2) + (intY ** 2)) | ||
|
||
def angleBetweenChars(firstChar, secondChar): | ||
fltAdj = float(abs(firstChar.intCenterX - secondChar.intCenterX)) | ||
fltOpp = float(abs(firstChar.intCenterY - secondChar.intCenterY)) | ||
|
||
if fltAdj != 0.0: | ||
fltAngleInRad = math.atan(fltOpp / fltAdj) | ||
else: | ||
fltAngleInRad = 1.5708 | ||
|
||
fltAngleInDeg = fltAngleInRad * (180.0 / math.pi) | ||
|
||
return fltAngleInDeg | ||
|
||
def removeInnerOverlappingChars(listOfMatchingChars): | ||
listOfMatchingCharsWithInnerCharRemoved = list(listOfMatchingChars) | ||
|
||
for currentChar in listOfMatchingChars: | ||
for otherChar in listOfMatchingChars: | ||
if currentChar != otherChar: | ||
if distanceBetweenChars(currentChar, otherChar) < (currentChar.fltDiagonalSize * min_d_s_m_a): | ||
if currentChar.intBoundingRectArea < otherChar.intBoundingRectArea: | ||
if currentChar in listOfMatchingCharsWithInnerCharRemoved: | ||
listOfMatchingCharsWithInnerCharRemoved.remove(currentChar) | ||
else: | ||
if otherChar in listOfMatchingCharsWithInnerCharRemoved: | ||
listOfMatchingCharsWithInnerCharRemoved.remove(otherChar) | ||
return listOfMatchingCharsWithInnerCharRemoved | ||
|
||
def recognizeCharsInPlate(imgThresh, listOfMatchingChars): | ||
strChars = "" | ||
|
||
height, width = imgThresh.shape | ||
|
||
imgThreshColor = np.zeros((height, width, 3), np.uint8) | ||
|
||
listOfMatchingChars.sort(key = lambda matchingChar: matchingChar.intCenterX) | ||
|
||
cv2.cvtColor(imgThresh, cv2.COLOR_GRAY2BGR, imgThreshColor) | ||
|
||
for currentChar in listOfMatchingChars: | ||
pt1 = (currentChar.intBoundingRectX, currentChar.intBoundingRectY) | ||
pt2 = ((currentChar.intBoundingRectX + currentChar.intBoundingRectWidth), (currentChar.intBoundingRectY + currentChar.intBoundingRectHeight)) | ||
|
||
cv2.rectangle(imgThreshColor, pt1, pt2, Main.SCALAR_GREEN, 2) | ||
|
||
imgROI = imgThresh[currentChar.intBoundingRectY : currentChar.intBoundingRectY + currentChar.intBoundingRectHeight, | ||
currentChar.intBoundingRectX : currentChar.intBoundingRectX + currentChar.intBoundingRectWidth] | ||
|
||
imgROIResized = cv2.resize(imgROI, (r_c_i_w, r_c_i_h)) | ||
|
||
npaROIResized = imgROIResized.reshape((1, r_c_i_w * r_c_i_h)) | ||
|
||
npaROIResized = np.float32(npaROIResized) | ||
|
||
retval, npaResults, neigh_resp, dists = kNearest.findNearest(npaROIResized, k = 1) | ||
|
||
strCurrentChar = str(chr(int(npaResults[0][0]))) | ||
|
||
strChars = strChars + strCurrentChar | ||
|
||
return strChars |
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,98 @@ | ||
# DetectPlates.py | ||
|
||
import cv2 | ||
import numpy as np | ||
import math | ||
import Main | ||
import random | ||
|
||
import Preprocess | ||
import DetectChars | ||
import PossiblePlate | ||
import PossibleChar | ||
|
||
PLATE_WIDTH_PADDING_FACTOR = 1.3 | ||
PLATE_HEIGHT_PADDING_FACTOR = 1.5 | ||
|
||
|
||
def findPossibleCharsInScene(imgThresh): | ||
listOfPossibleChars = [] | ||
|
||
intCountOfPossibleChars = 0 | ||
|
||
imgThreshCopy = imgThresh.copy() | ||
|
||
contours, npaHierarchy = cv2.findContours(imgThreshCopy, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) | ||
|
||
height, width = imgThresh.shape | ||
imgContours = np.zeros((height, width, 3), np.uint8) | ||
|
||
for i in range(0, len(contours)): | ||
|
||
possibleChar = PossibleChar.PossibleChar(contours[i]) | ||
|
||
if DetectChars.checkIfPossibleChar(possibleChar): | ||
intCountOfPossibleChars = intCountOfPossibleChars + 1 | ||
listOfPossibleChars.append(possibleChar) | ||
return listOfPossibleChars | ||
|
||
|
||
def extractPlate(imgOriginal, listOfMatchingChars): | ||
possiblePlate = PossiblePlate.PossiblePlate() | ||
|
||
listOfMatchingChars.sort(key = lambda matchingChar: matchingChar.intCenterX) | ||
|
||
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 | ||
|
||
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 | ||
|
||
fltAverageCharHeight = intTotalOfCharHeights / len(listOfMatchingChars) | ||
|
||
intPlateHeight = int(fltAverageCharHeight * PLATE_HEIGHT_PADDING_FACTOR) | ||
|
||
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) | ||
|
||
possiblePlate.rrLocationOfPlateInScene = ( tuple(ptPlateCenter), (intPlateWidth, intPlateHeight), fltCorrectionAngleInDeg ) | ||
|
||
rotationMatrix = cv2.getRotationMatrix2D(tuple(ptPlateCenter), fltCorrectionAngleInDeg, 1.0) | ||
|
||
height, width, numChannels = imgOriginal.shape | ||
|
||
imgRotated = cv2.warpAffine(imgOriginal, rotationMatrix, (width, height)) | ||
|
||
imgCropped = cv2.getRectSubPix(imgRotated, (intPlateWidth, intPlateHeight), tuple(ptPlateCenter)) | ||
|
||
possiblePlate.imgPlate = imgCropped | ||
|
||
return possiblePlate | ||
|
||
def detectPlatesInScene(imgOriginalScene): | ||
listOfPossiblePlates = [] | ||
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() | ||
imgGrayscaleScene, imgThreshScene = Preprocess.preprocess(imgOriginalScene) | ||
listOfPossibleCharsInScene = findPossibleCharsInScene(imgThreshScene) | ||
listOfListsOfMatchingCharsInScene = DetectChars.findListOfListsOfMatchingChars(listOfPossibleCharsInScene) | ||
print(5) | ||
for listOfMatchingChars in listOfListsOfMatchingCharsInScene: | ||
possiblePlate = extractPlate(imgOriginalScene, listOfMatchingChars) | ||
|
||
if possiblePlate.imgPlate is not None: | ||
listOfPossiblePlates.append(possiblePlate) | ||
return listOfPossiblePlates |
Oops, something went wrong.