def process(img): # this folder is used to save the image # temp_folder = 'C:\\Users\\PC\\Desktop\\temp\\' # ap = argparse.ArgumentParser() # ap.add_argument("-i", "--image", type=str, required=True, help="path to image") # args = vars(ap.parse_args()) # img = cv2.imread(args["image"]) # cv2.imshow('original', img) # cv2.imwrite(temp_folder + '1 - original.png', img) # hsv transform - value = gray image # img = cv2.imread(img_location) hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) hue, saturation, value = cv2.split(hsv) # cv2.imshow('HSV',hsv) # cv2.imwrite('HSV.jpg',hsv) # cv2.imshow('gray', value) # cv2.imwrite(temp_folder + '2 - gray.png', value) # kernel to use for morphological operations kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) # applying topHat/blackHat operations topHat = cv2.morphologyEx(value, cv2.MORPH_TOPHAT, kernel) blackHat = cv2.morphologyEx(value, cv2.MORPH_BLACKHAT, kernel) #cv2.imshow('topHat', topHat) #cv2.imshow('blackHat', blackHat) # cv2.imwrite(temp_folder + '3 - topHat.png', topHat) # cv2.imwrite(temp_folder + '4 - blackHat.png', blackHat) # add and subtract between morphological operations add = cv2.add(value, topHat) subtract = cv2.subtract(add, blackHat) # cv2.imshow('subtract', subtract) # cv2.imwrite(temp_folder + '5 - subtract.png', subtract) # applying gaussian blur on subtract image blur = cv2.GaussianBlur(subtract, (5, 5), 0) # cv2.imshow('blur', blur) # cv2.imwrite(temp_folder + '6 - blur.png', blur) # thresholding thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 19, 9) # cv2.imshow('thresh', thresh) # cv2.imwrite(temp_folder + '7 - thresh.png', thresh) # check for contours on thresh imageContours, contours, hierarchy = cv2.findContours( thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) # get height and width height, width = thresh.shape # create a numpy array with shape given by threshed image value dimensions imageContours = np.zeros((height, width, 3), dtype=np.uint8) # list and counter of possible chars possibleChars = [] countOfPossibleChars = 0 # loop to check if any (possible) char is found for i in range(0, len(contours)): # draw contours based on actual found contours of thresh image cv2.drawContours(imageContours, contours, i, (255, 255, 255)) # retrieve a possible char by the result ifChar class give us possibleChar = Functions.ifChar(contours[i]) # by computing some values (area, width, height, aspect ratio) possibleChars list is being populated if Functions.checkIfChar(possibleChar) is True: countOfPossibleChars = countOfPossibleChars + 1 possibleChars.append(possibleChar) # cv2.imshow("contours", imageContours) # cv2.imwrite(temp_folder + '8 - imageContours.png', imageContours) imageContours = np.zeros((height, width, 3), np.uint8) ctrs = [] # populating ctrs list with each char of possibleChars for char in possibleChars: ctrs.append(char.contour) # using values from ctrs to draw new contours cv2.drawContours(imageContours, ctrs, -1, (255, 255, 255)) # cv2.imshow("contoursPossibleChars", imageContours) # cv2.imwrite(temp_folder + '9 - contoursPossibleChars.png', imageContours) plates_list = [] listOfListsOfMatchingChars = [] for possibleC in possibleChars: # the purpose of this function is, given a possible char and a big list of possible chars, # find all chars in the big list that are a match for the single possible char, and return those matching chars as a list def matchingChars(possibleC, possibleChars): listOfMatchingChars = [] # if the char we attempting to find matches for is the exact same char as the char in the big list we are currently checking # then we should not include it in the list of matches b/c that would end up double including the current char # so do not add to list of matches and jump back to top of for loop for possibleMatchingChar in possibleChars: if possibleMatchingChar == possibleC: continue # compute stuff to see if chars are a match distanceBetweenChars = Functions.distanceBetweenChars( possibleC, possibleMatchingChar) angleBetweenChars = Functions.angleBetweenChars( possibleC, possibleMatchingChar) changeInArea = float( abs(possibleMatchingChar.boundingRectArea - possibleC.boundingRectArea)) / float( possibleC.boundingRectArea) changeInWidth = float( abs(possibleMatchingChar.boundingRectWidth - possibleC.boundingRectWidth)) / float( possibleC.boundingRectWidth) changeInHeight = float( abs(possibleMatchingChar.boundingRectHeight - possibleC.boundingRectHeight)) / float( possibleC.boundingRectHeight) # check if chars match if distanceBetweenChars < (possibleC.diagonalSize * 5) and \ angleBetweenChars < 12.0 and \ changeInArea < 0.5 and \ changeInWidth < 0.8 and \ changeInHeight < 0.2: listOfMatchingChars.append(possibleMatchingChar) return listOfMatchingChars # here we are re-arranging the one big list of chars into a list of lists of matching chars # the chars that are not found to be in a group of matches do not need to be considered further listOfMatchingChars = matchingChars(possibleC, possibleChars) listOfMatchingChars.append(possibleC) # if current possible list of matching chars is not long enough to constitute a possible plate # jump back to the top of the for loop and try again with next char if len(listOfMatchingChars) < 3: continue # here the current list passed test as a "group" or "cluster" of matching chars listOfListsOfMatchingChars.append(listOfMatchingChars) # remove the current list of matching chars from the big list so we don't use those same chars twice, # make sure to make a new big list for this since we don't want to change the original big list listOfPossibleCharsWithCurrentMatchesRemoved = list( set(possibleChars) - set(listOfMatchingChars)) recursiveListOfListsOfMatchingChars = [] for recursiveListOfMatchingChars in recursiveListOfListsOfMatchingChars: listOfListsOfMatchingChars.append(recursiveListOfMatchingChars) break imageContours = np.zeros((height, width, 3), np.uint8) for listOfMatchingChars in listOfListsOfMatchingChars: contoursColor = (255, 0, 255) contours = [] for matchingChar in listOfMatchingChars: contours.append(matchingChar.contour) cv2.drawContours(imageContours, contours, -1, contoursColor) # cv2.imshow("finalContours", imageContours) # cv2.imwrite(temp_folder + '10 - finalContours.png', imageContours) for listOfMatchingChars in listOfListsOfMatchingChars: possiblePlate = Functions.PossiblePlate() # sort chars from left to right based on x position listOfMatchingChars.sort(key=lambda matchingChar: matchingChar.centerX) # calculate the center point of the plate plateCenterX = ( listOfMatchingChars[0].centerX + listOfMatchingChars[len(listOfMatchingChars) - 1].centerX) / 2.0 plateCenterY = ( listOfMatchingChars[0].centerY + listOfMatchingChars[len(listOfMatchingChars) - 1].centerY) / 2.0 plateCenter = plateCenterX, plateCenterY # calculate plate width and height plateWidth = int( (listOfMatchingChars[len(listOfMatchingChars) - 1].boundingRectX + listOfMatchingChars[len(listOfMatchingChars) - 1].boundingRectWidth - listOfMatchingChars[0].boundingRectX) * 1.3) totalOfCharHeights = 0 for matchingChar in listOfMatchingChars: totalOfCharHeights = totalOfCharHeights + matchingChar.boundingRectHeight averageCharHeight = totalOfCharHeights / len(listOfMatchingChars) plateHeight = int(averageCharHeight * 1.5) # calculate correction angle of plate region opposite = listOfMatchingChars[len( listOfMatchingChars) - 1].centerY - listOfMatchingChars[0].centerY hypotenuse = Functions.distanceBetweenChars( listOfMatchingChars[0], listOfMatchingChars[len(listOfMatchingChars) - 1]) correctionAngleInRad = math.asin(opposite / hypotenuse) correctionAngleInDeg = correctionAngleInRad * (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(plateCenter), (plateWidth, plateHeight), correctionAngleInDeg) # get the rotation matrix for our calculated correction angle rotationMatrix = cv2.getRotationMatrix2D(tuple(plateCenter), correctionAngleInDeg, 1.0) height, width, numChannels = img.shape # rotate the entire image imgRotated = cv2.warpAffine(img, rotationMatrix, (width, height)) # crop the image/plate detected imgCropped = cv2.getRectSubPix(imgRotated, (plateWidth, plateHeight), tuple(plateCenter)) # copy the cropped plate image into the applicable member variable of the possible plate possiblePlate.Plate = imgCropped # cv2.imshow('Plate',imgCropped) cv2.imwrite('./static/outputs/NumberPlate.jpg', imgCropped) imgT = imgCropped # populate plates_list with the detected plate if possiblePlate.Plate is not None: plates_list.append(possiblePlate) # draw a ROI on the original image for i in range(0, len(plates_list)): # finds the four vertices of a rotated rect - it is useful to draw the rectangle. p2fRectPoints = cv2.boxPoints( plates_list[i].rrLocationOfPlateInScene) # roi rectangle colour rectColour = (0, 255, 0) cv2.line(imageContours, tuple(p2fRectPoints[0]), tuple(p2fRectPoints[1]), rectColour, 2) cv2.line(imageContours, tuple(p2fRectPoints[1]), tuple(p2fRectPoints[2]), rectColour, 2) cv2.line(imageContours, tuple(p2fRectPoints[2]), tuple(p2fRectPoints[3]), rectColour, 2) cv2.line(imageContours, tuple(p2fRectPoints[3]), tuple(p2fRectPoints[0]), rectColour, 2) cv2.line(img, tuple(p2fRectPoints[0]), tuple(p2fRectPoints[1]), rectColour, 2) cv2.line(img, tuple(p2fRectPoints[1]), tuple(p2fRectPoints[2]), rectColour, 2) cv2.line(img, tuple(p2fRectPoints[2]), tuple(p2fRectPoints[3]), rectColour, 2) cv2.line(img, tuple(p2fRectPoints[3]), tuple(p2fRectPoints[0]), rectColour, 2) imageContours = cv2.cvtColor(imageContours, cv2.COLOR_BGR2GRAY) sobel = cv2.Sobel(imageContours, cv2.CV_8U, 1, 0, ksize=3) element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 1)) element2 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) dilation = cv2.dilate(sobel, element2, iterations=1) erosion = cv2.erode(dilation, element1, iterations=1) dilation2 = cv2.dilate(erosion, element2, iterations=10) #cv2.imshow("detected", dilation2) # cv2.imwrite(temp_folder + '11 - detected.png', imageContours) #cv2.imshow("detectedOriginal", img) # cv2.imwrite(temp_folder + '12 - detectedOriginal.png', img) # cv2.imshow("plate", plates_list[i].Plate) # cv2.imwrite(temp_folder + '13 - plate.png', plates_list[i].Plate) return dilation2
def read(img): #args = vars(ap.parse_args()) #img = cv2.imread(args["image"]) retorno = "" # cv2.imshow('original', img) # cv2.imwrite(temp_folder + '1 - original.png', img) # hsv transform - value = gray image hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) hue, saturation, value = cv2.split(hsv) # cv2.imshow('gray', value) # cv2.imwrite(temp_folder + '2 - gray.png', value) # kernel a ser usado para operações morfológicas kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) # aplicação de operações topHat / blackHat topHat = cv2.morphologyEx(value, cv2.MORPH_TOPHAT, kernel) blackHat = cv2.morphologyEx(value, cv2.MORPH_BLACKHAT, kernel) # cv2.imshow('topHat', topHat) # cv2.imshow('blackHat', blackHat) # cv2.imwrite(temp_folder + '3 - topHat.png', topHat) # cv2.imwrite(temp_folder + '4 - blackHat.png', blackHat) # adicionar e subtrair entre operações morfológicas add = cv2.add(value, topHat) subtract = cv2.subtract(add, blackHat) # cv2.imshow('subtract', subtract) # cv2.imwrite(temp_folder + '5 - subtract.png', subtract) # aplicação de desfoque gaussiano na subtração da imagem blur = cv2.GaussianBlur(subtract, (5, 5), 0) # cv2.imshow('blur', blur) # cv2.imwrite(temp_folder + '6 - blur.png', blur) # thresholding thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 19, 9) # cv2.imshow('thresh', thresh) # cv2.imwrite(temp_folder + '7 - thresh.png', thresh) # cv2.findCountours() function changed from OpenCV3 to OpenCV4: now it have only two parameters instead of 3 cv2MajorVersion = cv2.__version__.split(".")[0] # verifique se há contornos na imagem if int(cv2MajorVersion) >= 4: contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) else: imageContours, contours, hierarchy = cv2.findContours( thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) # obter altura e largura height, width = thresh.shape # crie uma matriz numpy com a forma dada pelas dimensões do valor da imagem combinada imageContours = np.zeros((height, width, 3), dtype=np.uint8) # lista e contador de caracteres possíveis possibleChars = [] countOfPossibleChars = 0 # loop para verificar se algum (possível) caractere é encontrado for i in range(0, len(contours)): # desenhe contornos com base nos contornos encontrados reais da imagem thresh cv2.drawContours(imageContours, contours, i, (255, 255, 255)) # recuperar um possível caractere pelo resultado da classe ifChar nos fornecer possibleChar = Functions.ifChar(contours[i]) #calculando alguns valores (área, largura, altura, proporção) possíveis if Functions.checkIfChar(possibleChar) is True: countOfPossibleChars = countOfPossibleChars + 1 possibleChars.append(possibleChar) # cv2.imshow("contours", imageContours) # cv2.imwrite(temp_folder + '8 - imageContours.png', imageContours) # usando valores de ctrs para desenhar novos contornos imageContours = np.zeros((height, width, 3), np.uint8) ctrs = [] # populating ctrs list with each char of possibleChars for char in possibleChars: ctrs.append(char.contour) # using values from ctrs to draw new contours cv2.drawContours(imageContours, ctrs, -1, (255, 255, 255)) # cv2.imshow("contoursPossibleChars", imageContours) # cv2.imwrite(temp_folder + '9 - contoursPossibleChars.png', imageContours) plates_list = [] listOfListsOfMatchingChars = [] for possibleC in possibleChars: # o objetivo desta função é, dado um possível caractere e uma grande lista de possíveis caracteres, # encontre todos os caracteres na grande lista que correspondem ao único #caractere possível e retorne esses caracteres como uma lista def matchingChars(possibleC, possibleChars): listOfMatchingChars = [] # se o caractere para o qual estamos tentando encontrar correspondências for exatamente o mesmo caractere que o caractere na grande lista que estamos verificando # então não devemos incluí-lo na lista de correspondências b / c que acabariam em dobro, incluindo o caractere atual # então não adicione à lista de correspondências e volte ao topo do loop for for possibleMatchingChar in possibleChars: if possibleMatchingChar == possibleC: continue # calcular coisas para ver se os caracteres são uma correspondência distanceBetweenChars = Functions.distanceBetweenChars( possibleC, possibleMatchingChar) angleBetweenChars = Functions.angleBetweenChars( possibleC, possibleMatchingChar) changeInArea = float( abs(possibleMatchingChar.boundingRectArea - possibleC.boundingRectArea)) / float( possibleC.boundingRectArea) changeInWidth = float( abs(possibleMatchingChar.boundingRectWidth - possibleC.boundingRectWidth)) / float( possibleC.boundingRectWidth) changeInHeight = float( abs(possibleMatchingChar.boundingRectHeight - possibleC.boundingRectHeight)) / float( possibleC.boundingRectHeight) # verifique se os caracteres correspondem if distanceBetweenChars < (possibleC.diagonalSize * 5) and \ angleBetweenChars < 12.0 and \ changeInArea < 0.5 and \ changeInWidth < 0.8 and \ changeInHeight < 0.2: listOfMatchingChars.append(possibleMatchingChar) return listOfMatchingChars # aqui estamos reorganizando a grande lista de caracteres em uma lista de listas de caracteres correspondentes # os caracteres que não foram encontrados em um grupo de correspondências não precisam ser considerados mais listOfMatchingChars = matchingChars(possibleC, possibleChars) listOfMatchingChars.append(possibleC) if len(listOfMatchingChars) < 6: continue listOfListsOfMatchingChars.append(listOfMatchingChars) # remova a lista atual de caracteres correspondentes da grande lista para não usarmos os mesmos caracteres duas vezes, # certifique-se de criar uma nova grande lista para isso, pois não queremos alterar a grande lista original #listOfPossibleCharsWithCurrentMatchesRemoved = list(set(possibleChars) - set(listOfMatchingChars)) recursiveListOfListsOfMatchingChars = [] for recursiveListOfMatchingChars in recursiveListOfListsOfMatchingChars: listOfListsOfMatchingChars.append(recursiveListOfMatchingChars) break imageContours = np.zeros((height, width, 3), np.uint8) for listOfMatchingChars in listOfListsOfMatchingChars: contoursColor = (255, 0, 255) contours = [] for matchingChar in listOfMatchingChars: contours.append(matchingChar.contour) cv2.drawContours(imageContours, contours, -1, contoursColor) # cv2.imshow("finalContours", imageContours) # cv2.imwrite(temp_folder + '10 - finalContours.png', imageContours) for listOfMatchingChars in listOfListsOfMatchingChars: possiblePlate = Functions.PossiblePlate() # sort chars from left to right based on x position listOfMatchingChars.sort(key=lambda matchingChar: matchingChar.centerX) # calcular o ponto central da placa plateCenterX = ( listOfMatchingChars[0].centerX + listOfMatchingChars[len(listOfMatchingChars) - 1].centerX) / 2.0 plateCenterY = ( listOfMatchingChars[0].centerY + listOfMatchingChars[len(listOfMatchingChars) - 1].centerY) / 2.0 plateCenter = plateCenterX, plateCenterY # calcular largura e altura da placa plateWidth = int( (listOfMatchingChars[len(listOfMatchingChars) - 1].boundingRectX + listOfMatchingChars[len(listOfMatchingChars) - 1].boundingRectWidth - listOfMatchingChars[0].boundingRectX) * 1.3) totalOfCharHeights = 0 for matchingChar in listOfMatchingChars: totalOfCharHeights = totalOfCharHeights + matchingChar.boundingRectHeight averageCharHeight = totalOfCharHeights / len(listOfMatchingChars) plateHeight = int(averageCharHeight * 1.5) # calcular o ângulo de correção da região da placa opposite = listOfMatchingChars[len( listOfMatchingChars) - 1].centerY - listOfMatchingChars[0].centerY hypotenuse = Functions.distanceBetweenChars( listOfMatchingChars[0], listOfMatchingChars[len(listOfMatchingChars) - 1]) correctionAngleInRad = math.asin(opposite / hypotenuse) correctionAngleInDeg = correctionAngleInRad * (180.0 / math.pi) # empacote o ponto central, a largura e a altura da região da placa e o ângulo de correção na variável de membro retira da placa possiblePlate.rrLocationOfPlateInScene = (tuple(plateCenter), (plateWidth, plateHeight), correctionAngleInDeg) # obtenha a matriz de rotação para o ângulo de correção calculado rotationMatrix = cv2.getRotationMatrix2D(tuple(plateCenter), correctionAngleInDeg, 1.0) height, width, numChannels = img.shape # girar a imagem inteira imgRotated = cv2.warpAffine(img, rotationMatrix, (width, height)) # cortar a imagem / placa detecta imgCropped = cv2.getRectSubPix(imgRotated, (plateWidth, plateHeight), tuple(plateCenter)) # copie a imagem da placa cortada na variável de membro aplicável da placa possível possiblePlate.Plate = imgCropped # preenche a plates_list com as placas detectadas if possiblePlate.Plate is not None: plates_list.append(possiblePlate) # Desenha um ROI na imagem original for i in range(0, len(plates_list)): # finds the four vertices of a rotated rect - it is useful to draw the rectangle. p2fRectPoints = cv2.boxPoints( plates_list[i].rrLocationOfPlateInScene) # roi rectangle colour rectColour = (0, 255, 0) cv2.line(imageContours, tuple(p2fRectPoints[0]), tuple(p2fRectPoints[1]), rectColour, 2) cv2.line(imageContours, tuple(p2fRectPoints[1]), tuple(p2fRectPoints[2]), rectColour, 2) cv2.line(imageContours, tuple(p2fRectPoints[2]), tuple(p2fRectPoints[3]), rectColour, 2) cv2.line(imageContours, tuple(p2fRectPoints[3]), tuple(p2fRectPoints[0]), rectColour, 2) cv2.line(img, tuple(p2fRectPoints[0]), tuple(p2fRectPoints[1]), rectColour, 2) cv2.line(img, tuple(p2fRectPoints[1]), tuple(p2fRectPoints[2]), rectColour, 2) cv2.line(img, tuple(p2fRectPoints[2]), tuple(p2fRectPoints[3]), rectColour, 2) cv2.line(img, tuple(p2fRectPoints[3]), tuple(p2fRectPoints[0]), rectColour, 2) retorno = getCharactres(plates_list[i].Plate) cv2.waitKey(0) return retorno
thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) height, width = thresh.shape imageContours = np.zeros((height, width, 3), dtype=np.uint8) possibleChars = [] countOfPossibleChars = 0 for i in range(0, len(contours)): cv2.drawContours(imageContours, contours, i, (255, 255, 255)) possibleChar = Functions.ifChar(contours[i]) if Functions.checkIfChar(possibleChar) is True: countOfPossibleChars = countOfPossibleChars + 1 possibleChars.append(possibleChar) imageContours = np.zeros((height, width, 3), np.uint8) ctrs = [] for char in possibleChars: ctrs.append(char.contour) cv2.drawContours(imageContours, ctrs, -1, (255, 255, 255)) plates_list = [] listOfListsOfMatchingChars = []
height, width = thresh.shape imageContours = np.zeros((height, width, 3), dtype=np.uint8) possibleChars = [] countOfPossibleChars = 0 for i in range(0, len(contours)): cv2.drawContours( imageContours, contours, i, (255, 255, 255)) # draw contours based on actual found contours of thresh image possibleChar = Functions.ifChar( contours[i] ) # retrieve a possible char by the result ifChar class give us if Functions.checkIfChar( possibleChar ) is True: # by computing some values (area, width, height, aspect ratio) possibleChars list is being populated countOfPossibleChars = countOfPossibleChars + 1 possibleChars.append(possibleChar) imageContours = np.zeros((height, width, 3), np.uint8) ctrs = [] for char in possibleChars: ctrs.append(char.contour) # using values from ctrs to draw new contours cv2.drawContours(imageContours, ctrs, -1, (255, 255, 255)) #cv2.imshow("contoursPossibleChars", imageContours) cv2.imwrite(temp_folder + '9 - contoursPossibleChars.png', imageContours) plates_list = []