背景
使用到的数据
接到一个需求,利用myscript笔迹文件的信息,在对应图片右下角打上标记。
作答图片如下
而书写这图片所生成的myscript解析后的笔迹文件的内容数据结构如下
{
"type": "Text",
"bounding-box": {
"x": 28.9970188,
"y": 86.0071945,
"width": 77.0569305,
"height": 12.1241608
},
"label": "核酸, DNA, RNA",
"words": [
{
"label": "核酸",
"candidates": [
"核酸",
"核䘒"
],
"first-char": 0,
"last-char": 1,
"bounding-box": {
"x": 28.9970188,
"y": 86.2018967,
"width": 20.7964153,
"height": 11.92
},
},
...
"chars": [
{
"label": "核",
"word": 0,
"grid": [
{
"x": 29.4614334,
"y": 86.8824234
},
{
"x": 38.2922058,
"y": 86.8824234
},
{
"x": 38.2922058,
"y": 100.476776
},
{
"x": 29.4614334,
"y": 100.476776
}
],
"bounding-box": {
"x": 28.9970188,
"y": 86.2018967,
"width": 9.08084297,
"height": 11.9294586
},
},
...
而标记的图片如下
补充的背景信息
- 作答图片的大小是自适应生成的,也就是会随着使用者的书写而调整
- 作答图片的四边在生成时会填充上宽度为60pixel的白边
- 笔迹文件的坐标并不是在图片上的绝对坐标,只是一个相对坐标,但这个坐标是使用笛卡尔坐标系
初步思路
- 由于笔迹文件的坐标只是相对坐标,如果想在作答图片上进行正确标记的话,必须将笔迹文件坐标转化为作答图片上的坐标。
- 根据bounding-box字段求得整个作答区域的长宽与作答图片的长宽,求得比例系数
- 利用比例系数,求得在作答图片的坐标
- 对求得的新作答图片坐标上的长与宽进行60pixel的平移
逻辑实现
def _convert_bounding_boxes(self, original_boxes, writing_area_coords, image_size):
"""
严格矩形书写区域的坐标转换(等比例缩放 + 边框)
"""
# 1. 计算可用内容区域(减去 120px 边框)
border_padding = 60
content_width = image_size[0] - 2 * border_padding
content_height = image_size[1] - 2 * border_padding
# 2. 提取书写区域坐标
tl = writing_area_coords['top_left']
tr = writing_area_coords['top_right']
bl = writing_area_coords['bottom_left']
# 3. 计算原始书写区域的宽度和高度
original_width = tr[0] - tl[0]
original_height = bl[1] - tl[1]
# 4. 计算统一缩放比例(取宽高比例的最小值,避免溢出)
width_ratio = content_width / original_width
height_ratio = content_height / original_height
uniform_ratio = min(width_ratio, height_ratio)
# 5. 计算居中偏移量(让内容在可用区域内居中)
scaled_width = original_width * uniform_ratio
scaled_height = original_height * uniform_ratio
offset_x = (content_width - scaled_width) / 2 + border_padding # 水平居中
offset_y = (content_height - scaled_height) / 2 + border_padding # 垂直居中
converted_boxes = []
for box in original_boxes:
if box["bounding-box"] is not None:
bbox = box['bounding-box']
# 6. 计算偏移量(相对于书写区域左上角)
original_x = bbox['x'] - tl[0]
original_y = bbox['y'] - tl[1]
# 7. 应用统一缩放
new_x = original_x * uniform_ratio + offset_x
new_y = original_y * uniform_ratio + offset_y
new_width = bbox['width'] * uniform_ratio
new_height = bbox['height'] * uniform_ratio
# 8. 四舍五入到整数像素(减少浮点误差)
converted_box = box.copy()
converted_box['bounding-box'] = {
'x': new_x,
'y': new_y,
'width': new_width,
'height': new_height
}
converted_boxes.append(converted_box)
else:
converted_box = box.copy()
converted_box['bounding-box'] = {
'x': 0,
'y': 0,
'width': 0,
'height': 0
}
converted_boxes.append(converted_box)
return converted_boxes
补充
- 使用居中会令效果更好
# 5. 计算居中偏移量(让内容在可用区域内居中)
scaled_width = original_width * uniform_ratio
scaled_height = original_height * uniform_ratio
offset_x = (content_width - scaled_width) / 2 + border_padding # 水平居中
offset_y = (content_height - scaled_height) / 2 + border_padding # 垂直居中
- 是否使用统一比例,要根据实际情况来调,在这里使用统一比例效果会好一丢丢
# 4. 计算统一缩放比例(取宽高比例的最小值,避免溢出)
width_ratio = content_width / original_width
height_ratio = content_height / original_height
uniform_ratio = min(width_ratio, height_ratio)
测试效果
可以看到可以成功框选出每个词语的最后一个字,这就可以利用坐标进行标记了