| import sys |
| sys.dont_write_bytecode = True |
|
|
| import cv2 |
| import numpy |
| import pyclipper |
|
|
| from helper import onnxSessionBuild |
|
|
| pathModel = "./PP-OCRv6_medium_det/" |
|
|
| limitSideLength = 960 |
| limitSideLengthMax = 4000 |
| binaryThreshold = 0.2 |
| boxThreshold = 0.45 |
| unclipRatio = 1.4 |
| candidateMax = 3000 |
| sideMinimum = 3 |
|
|
| meanList = numpy.array([0.485, 0.456, 0.406], dtype=numpy.float32) |
| standardList = numpy.array([0.229, 0.224, 0.225], dtype=numpy.float32) |
|
|
| onnxSession = onnxSessionBuild(f"{pathModel}onnx/pp-ocrV6_medium_det.onnx") |
|
|
| def imageResize(image): |
| imageHeight, imageWidth = image.shape[0:2] |
|
|
| ratio = 1.0 |
|
|
| if max(imageHeight, imageWidth) > limitSideLength: |
| if imageHeight > imageWidth: |
| ratio = float(limitSideLength) / imageHeight |
| else: |
| ratio = float(limitSideLength) / imageWidth |
|
|
| resizeHeight = int(imageHeight * ratio) |
| resizeWidth = int(imageWidth * ratio) |
|
|
| if max(resizeHeight, resizeWidth) > limitSideLengthMax: |
| ratio = float(limitSideLengthMax) / max(resizeHeight, resizeWidth) |
|
|
| resizeHeight = int(resizeHeight * ratio) |
| resizeWidth = int(resizeWidth * ratio) |
|
|
| resizeHeight = max(int(round(resizeHeight / 32) * 32), 32) |
| resizeWidth = max(int(round(resizeWidth / 32) * 32), 32) |
|
|
| return cv2.resize(image, (resizeWidth, resizeHeight)) |
|
|
| def boxOrder(contour): |
| rectangle = cv2.minAreaRect(contour) |
|
|
| pointList = sorted(list(cv2.boxPoints(rectangle)), key=lambda point: point[0]) |
|
|
| index1 = 0 |
| index2 = 1 |
| index3 = 2 |
| index4 = 3 |
|
|
| if pointList[1][1] > pointList[0][1]: |
| index1 = 0 |
| index4 = 1 |
| else: |
| index1 = 1 |
| index4 = 0 |
|
|
| if pointList[3][1] > pointList[2][1]: |
| index2 = 2 |
| index3 = 3 |
| else: |
| index2 = 3 |
| index3 = 2 |
|
|
| return [pointList[index1], pointList[index2], pointList[index3], pointList[index4]], min(rectangle[1]) |
|
|
| def boxScore(probabilityMap, box): |
| mapHeight, mapWidth = probabilityMap.shape[0:2] |
|
|
| boxLocal = box.copy() |
|
|
| xMinimum = max(0, min(int(numpy.floor(box[:, 0].min())), mapWidth - 1)) |
| xMaximum = max(0, min(int(numpy.ceil(box[:, 0].max())), mapWidth - 1)) |
| yMinimum = max(0, min(int(numpy.floor(box[:, 1].min())), mapHeight - 1)) |
| yMaximum = max(0, min(int(numpy.ceil(box[:, 1].max())), mapHeight - 1)) |
|
|
| mask = numpy.zeros((yMaximum - yMinimum + 1, xMaximum - xMinimum + 1), dtype=numpy.uint8) |
|
|
| boxLocal[:, 0] = boxLocal[:, 0] - xMinimum |
| boxLocal[:, 1] = boxLocal[:, 1] - yMinimum |
|
|
| cv2.fillPoly(mask, boxLocal.reshape(1, -1, 2).astype(numpy.int32), 1) |
|
|
| return cv2.mean(probabilityMap[yMinimum:yMaximum + 1, xMinimum:xMaximum + 1], mask)[0] |
|
|
| def boxUnclip(box): |
| area = cv2.contourArea(box) |
| length = cv2.arcLength(box, True) |
|
|
| distance = area * unclipRatio / length |
|
|
| offsetObject = pyclipper.PyclipperOffset() |
|
|
| offsetObject.AddPath(box, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) |
|
|
| return numpy.array(offsetObject.Execute(distance)) |
|
|
| def inference(image): |
| resultList = [] |
|
|
| imageHeight, imageWidth = image.shape[0:2] |
| imageResized = imageResize(image) |
|
|
| tensor = (imageResized.astype(numpy.float32) / 255.0 - meanList) / standardList |
| tensor = numpy.expand_dims(tensor.transpose((2, 0, 1)), axis=0).astype(numpy.float32) |
|
|
| tensorOutputList = onnxSession.run(None, {"x": tensor}) |
|
|
| probabilityMap = tensorOutputList[0][0][0] |
| bitmap = (probabilityMap > binaryThreshold).astype(numpy.uint8) |
|
|
| scaleWidth = imageWidth / float(bitmap.shape[1]) |
| scaleHeight = imageHeight / float(bitmap.shape[0]) |
|
|
| contourList, hierarchy = cv2.findContours(bitmap * 255, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) |
|
|
| for a in range(min(len(contourList), candidateMax)): |
| pointList, sideLength = boxOrder(contourList[a]) |
|
|
| if sideLength < sideMinimum: |
| continue |
|
|
| score = boxScore(probabilityMap, numpy.array(pointList).reshape(-1, 2)) |
|
|
| if score < boxThreshold: |
| continue |
|
|
| pointList, sideLength = boxOrder(boxUnclip(numpy.array(pointList)).reshape(-1, 1, 2)) |
|
|
| if sideLength < sideMinimum + 2: |
| continue |
|
|
| coordinateList = [] |
|
|
| for b in range(len(pointList)): |
| x = max(0, min(int(round(pointList[b][0] * scaleWidth)), imageWidth)) |
| y = max(0, min(int(round(pointList[b][1] * scaleHeight)), imageHeight)) |
|
|
| coordinateList.append([x, y]) |
|
|
| resultList.append({ |
| "score": score, |
| "coordinate": coordinateList |
| }) |
|
|
| return resultList |
|
|
| image = cv2.imread(sys.argv[1]) |
|
|
| itemList = inference(image) |
|
|
| for a in range(len(itemList)): |
| print(f"{itemList[a]['score']:.6f} | {itemList[a]['coordinate']}") |
|
|