将边界框合并为一个

时间:2019-04-09 12:58:15

标签: python python-3.x machine-learning deep-learning computer-vision

我是python的新手,我使用的是快速入门:使用计算机视觉中的REST API和Python提取打印文本(OCR),以便在Sales Fliers中检测文本。因此,该算法的坐标为Ymin,XMax,Ymin,和Xmax,并为每行文本绘制一个边界框,它显示在下一张图像中。

enter image description here

但是我想对附近的文本进行分组,使其具有单个定界框架。因此对于上面的图片,它将有2个包含最接近的文本的边框。

下面的代码提供Ymin,XMax,Ymin和Xmax作为坐标,并为每行文本绘制一个边框。

import requests
# If you are using a Jupyter notebook, uncomment the following line.
%matplotlib inline
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from PIL import Image
from io import BytesIO

# Replace <Subscription Key> with your valid subscription key.
subscription_key = "f244aa59ad4f4c05be907b4e78b7c6da"
assert subscription_key

vision_base_url = "https://westcentralus.api.cognitive.microsoft.com/vision/v2.0/"

ocr_url = vision_base_url + "ocr"

# Set image_url to the URL of an image that you want to analyze.
image_url = "https://cdn-ayb.akinon.net/cms/2019/04/04/e494dce0-1e80-47eb-96c9-448960a71260.jpg"

headers = {'Ocp-Apim-Subscription-Key': subscription_key}
params  = {'language': 'unk', 'detectOrientation': 'true'}
data    = {'url': image_url}
response = requests.post(ocr_url, headers=headers, params=params, json=data)
response.raise_for_status()

analysis = response.json()

# Extract the word bounding boxes and text.
line_infos = [region["lines"] for region in analysis["regions"]]
word_infos = []
for line in line_infos:
    for word_metadata in line:
        for word_info in word_metadata["words"]:
            word_infos.append(word_info)
word_infos

# Display the image and overlay it with the extracted text.
plt.figure(figsize=(100, 20))
image = Image.open(BytesIO(requests.get(image_url).content))
ax = plt.imshow(image)
texts_boxes = []
texts = []
for word in word_infos:
    bbox = [int(num) for num in word["boundingBox"].split(",")]
    text = word["text"]
    origin = (bbox[0], bbox[1])
    patch  = Rectangle(origin, bbox[2], bbox[3], fill=False, linewidth=3, color='r')
    ax.axes.add_patch(patch)
    plt.text(origin[0], origin[1], text, fontsize=2, weight="bold", va="top")
#     print(bbox)
    new_box = [bbox[1], bbox[0], bbox[1]+bbox[3], bbox[0]+bbox[2]]
    texts_boxes.append(new_box)
    texts.append(text)
#     print(text)
plt.axis("off")
texts_boxes = np.array(texts_boxes)
texts_boxes

输出边界框

array([[  68,   82,  138,  321],
       [ 202,   81,  252,  327],
       [ 261,   81,  308,  327],
       [ 364,  112,  389,  182],
       [ 362,  192,  389,  305],
       [ 404,   98,  421,  317],
       [  92,  421,  146,  725],
       [  80,  738,  134, 1060],
       [ 209,  399,  227,  456],
       [ 233,  399,  250,  444],
       [ 257,  400,  279,  471],
       [ 281,  399,  298,  440],
       [ 286,  446,  303,  458],
       [ 353,  394,  366,  429]]

但是我想合并很近。

3 个答案:

答案 0 :(得分:1)

您可以在运行代码之前使用openCV并应用dilation和blackhat转换来处理图像

答案 1 :(得分:1)

谢谢@recnac,您的算法可以帮助我解决问题。

我的解决方法是这样。 生成一个新框,合并距离很近的文本框以获得一个新框。在其中有一个封闭的文字。

import numpy, scipy, matplotlib
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.optimize import differential_evolution
import warnings


points = numpy.array([(0, -0.0142294), (20, 0.0308458785714286), (50, 0.1091054), (100 ,0.2379176875), (200, 0.404354166666667)])
x = points[:,0]
y = points[:,1]

# rename to match previous example code below
xData = x
yData = y


def func(x, p1,p2):
  return p1*(1-numpy.exp(-p2*x))


# function for genetic algorithm to minimize (sum of squared error)
def sumOfSquaredError(parameterTuple):
    warnings.filterwarnings("ignore") # do not print warnings by genetic algorithm
    val = func(xData, *parameterTuple)
    return numpy.sum((yData - val) ** 2.0)


def generate_Initial_Parameters():
    # min and max used for bounds
    maxX = max(xData)
    minX = min(xData)
    maxY = max(yData)
    minY = min(yData)

    minAllData = min(minX, minY)
    maxAllData = min(maxX, maxY)

    parameterBounds = []
    parameterBounds.append([minAllData, maxAllData]) # search bounds for p1
    parameterBounds.append([minAllData, maxAllData]) # search bounds for p2

    # "seed" the numpy random number generator for repeatable results
    result = differential_evolution(sumOfSquaredError, parameterBounds, seed=3)
    return result.x

# by default, differential_evolution completes by calling curve_fit() using parameter bounds
geneticParameters = generate_Initial_Parameters()

# now call curve_fit without passing bounds from the genetic algorithm,
# just in case the best fit parameters are aoutside those bounds
fittedParameters, pcov = curve_fit(func, xData, yData, geneticParameters)
print('Fitted parameters:', fittedParameters)
print()

modelPredictions = func(xData, *fittedParameters) 

absError = modelPredictions - yData

SE = numpy.square(absError) # squared errors
MSE = numpy.mean(SE) # mean squared errors
RMSE = numpy.sqrt(MSE) # Root Mean Squared Error, RMSE
Rsquared = 1.0 - (numpy.var(absError) / numpy.var(yData))

print()
print('RMSE:', RMSE)
print('R-squared:', Rsquared)

print()


##########################################################
# graphics output section
def ModelAndScatterPlot(graphWidth, graphHeight):
    f = plt.figure(figsize=(graphWidth/100.0, graphHeight/100.0), dpi=100)
    axes = f.add_subplot(111)

    # first the raw data as a scatter plot
    axes.plot(xData, yData,  'D')

    # create data for the fitted equation plot
    xModel = numpy.linspace(min(xData), max(xData))
    yModel = func(xModel, *fittedParameters)

    # now the model as a line plot
    axes.plot(xModel, yModel)

    axes.set_xlabel('X Data') # X axis data label
    axes.set_ylabel('Y Data') # Y axis data label

    plt.show()
    plt.close('all') # clean up after using pyplot

graphWidth = 800
graphHeight = 600
ModelAndScatterPlot(graphWidth, graphHeight)

答案 2 :(得分:0)

您可以选中两个框(x_minx_maxy_miny_max)的边界,如果差异小于close_dist,则应合并到一个新盒子。然后在两个for循环中连续执行此操作:

from itertools import product

close_dist = 20

# common version
def should_merge(box1, box2):
    for i in range(2):
        for j in range(2):
            for k in range(2):
                if abs(box1[j * 2 + i] - box2[k * 2 + i]) <= close_dist:
                    return True, [min(box1[0], box2[0]), min(box1[1], box2[1]), max(box1[2], box2[2]),
                                  max(box1[3], box2[3])]
    return False, None


# use product, more concise
def should_merge2(box1, box2):
    a = (box1[0], box1[2]), (box1[1], box1[3])
    b = (box2[0], box2[2]), (box2[1], box2[3])

    if any(abs(a_v - b_v) <= close_dist for i in range(2) for a_v, b_v in product(a[i], b[i])):
        return True, [min(*a[0], *b[0]), min(*a[1], *b[1]), max(*a[0], *b[0]), max(*a[1], *b[1])]

    return False, None

def merge_box(boxes):
    for i, box1 in enumerate(boxes):
        for j, box2 in enumerate(boxes[i + 1:]):
            is_merge, new_box = should_merge(box1, box2)
            if is_merge:
                boxes[i] = None
                boxes[j] = new_box
                break

    boxes = [b for b in boxes if b]
    print(boxes)

测试代码:

boxes = [[68, 82, 138, 321],
         [202, 81, 252, 327],
         [261, 81, 308, 327],
         [364, 112, 389, 182],
         [362, 192, 389, 305],
         [404, 98, 421, 317],
         [92, 421, 146, 725],
         [80, 738, 134, 1060],
         [209, 399, 227, 456],
         [233, 399, 250, 444],
         [257, 400, 279, 471],
         [281, 399, 298, 440],
         [286, 446, 303, 458],
         [353, 394, 366, 429]]

print(merge_box(boxes))

输出:

[[286, 394, 366, 458], [261, 81, 421, 327], [404, 98, 421, 317], [80, 738, 134, 1060], [353, 394, 366, 429]]

无法进行视觉测试,请为我测试。

希望对您有所帮助,如果还有其他问题,请发表评论。 :)