使用数据矩阵作为基准来获得旋转角度

Use data matrix as a fiducial to obtain angle of rotation

提问人:Jon Zavialov 提问时间:7/27/2023 最后编辑:Jon Zavialov 更新时间:8/5/2023 访问量:193

问:

我有一堆图片,比如上面的那张。它们都包含一个数据矩阵,但不能保证它指向一个轴。尽管如此,无论它们如何旋转,我都可以非常可靠地读取这些矩阵。但是,我还需要旋转图像,使标签朝右朝上。我的思考过程是,我需要获得数据矩阵的旋转角度,以便我可以用 PIL 旋转图像以正确定位它。 返回矩阵包含的数据,以及一个矩形,我最初认为这是数据矩阵的边界框。为了测试这一点,我运行了以下代码,如上图所示:libdmtxpylibdmtx.decode

from PIL import Image
from pylibdmtx.pylibdmtx import decode

def segment_qr_code(image: Image.Image):
    data = decode(image)[0]
    print(data.rect)

if __name__ == "__main__":
    segment_qr_code(Image.open('<path to image>'))

不幸的是,此代码返回了 .因为高度是负数,所以我不认为它是数据矩阵的边界框,如果是,我不知道如何使用它来获得旋转角度。Rect(left=208, top=112, width=94, height=-9)

我的问题是,获得数据矩阵旋转角度的最佳方法是什么?我最初认为我可以用边界框裁剪图像,以获得数据矩阵的分割图像。然后,我可以使用图像阈值或轮廓来获得旋转角度。但是,我不确定如何获得正确的边界框,即使我这样做了,我也不知道如何使用阈值。我也宁愿不使用阈值,因为它并不总是准确的。数据矩阵的底部和左侧始终有一个实心边框,所以我认为可以将其用作对齐图像的基准点,但是我无法找到任何能够返回数据矩阵旋转角度的库。

我愿意接受任何建议。提前致谢。

Python OpenCV 处理 图像 阈值 Datamatrix

评论

0赞 fmw42 7/27/2023
白色的阈值。然后得到轮廓。使用 cv2.minAreaRect() 获取四边形的角度。
0赞 flakes 8/4/2023
你见过这个问题吗?似乎他们提供了一些解决方法。
0赞 flakes 8/4/2023
似乎他们合并修复程序的速度很慢。大约一年前,提交了一份 PR 来尝试解决这个问题 github.com/NaturalHistoryMuseum/pylibdmtx/pull/85

答:

3赞 Jon Zavialov 8/4/2023 #1

感谢@flakes的建议。结合 PR 和 issue 中的代码,我创建了以下解决方案:

from pylibdmtx.pylibdmtx import _region, _decoder, _image, _pixel_data, _decoded_matrix_region
from pylibdmtx.wrapper import c_ubyte_p, DmtxPackOrder, DmtxVector2, dmtxMatrix3VMultiplyBy, DmtxUndefined
from ctypes import cast, string_at
from collections import namedtuple
import numpy

_pack_order = {
    8: DmtxPackOrder.DmtxPack8bppK,
    16: DmtxPackOrder.DmtxPack16bppRGB,
    24: DmtxPackOrder.DmtxPack24bppRGB,
    32: DmtxPackOrder.DmtxPack32bppRGBX,
}
Decoded = namedtuple('Decoded', 'data rect')


def decode_with_region(image):
    results = []
    pixels, width, height, bpp = _pixel_data(image)
    with _image(cast(pixels, c_ubyte_p), width, height, _pack_order[bpp]) as img:
        with _decoder(img, 1) as decoder:
            while True:
                with _region(decoder, None) as region:
                    if not region:
                        break
                    else:
                        res = _decode_region(decoder, region)
                        if res:
                            open_cv_image = numpy.array(image)
                            # Convert RGB to BGR
                            open_cv_image = open_cv_image[:, :, ::-1].copy()
                            height, width, _ = open_cv_image.shape

                            topLeft = (res.rect['01']['x'], height - res.rect['01']['y'])
                            topRight = (res.rect['11']['x'], height - res.rect['11']['y'])
                            bottomRight = (res.rect['10']['x'], height - res.rect['10']['y'])
                            bottomLeft = (res.rect['00']['x'], height - res.rect['00']['y'])
                            results.append(Decoded(res.data, (topLeft, topRight, bottomRight, bottomLeft)))
    return results


def _decode_region(decoder, region):
    with _decoded_matrix_region(decoder, region, DmtxUndefined) as msg:
        if msg:
            vector00 = DmtxVector2()
            vector11 = DmtxVector2(1.0, 1.0)
            vector10 = DmtxVector2(1.0, 0.0)
            vector01 = DmtxVector2(0.0, 1.0)
            dmtxMatrix3VMultiplyBy(vector00, region.contents.fit2raw)
            dmtxMatrix3VMultiplyBy(vector11, region.contents.fit2raw)
            dmtxMatrix3VMultiplyBy(vector01, region.contents.fit2raw)
            dmtxMatrix3VMultiplyBy(vector10, region.contents.fit2raw)

            return Decoded(
                string_at(msg.contents.output),
                {
                    '00': {
                        'x': int((vector00.X) + 0.5),
                        'y': int((vector00.Y) + 0.5)
                    },
                    '01': {
                        'x': int((vector01.X) + 0.5),
                        'y': int((vector01.Y) + 0.5)
                    },
                    '10': {
                        'x': int((vector10.X) + 0.5),
                        'y': int((vector10.Y) + 0.5)
                    },
                    '11': {
                        'x': int((vector11.X) + 0.5),
                        'y': int((vector11.Y) + 0.5)
                    }
                }
            )
        else:
            return None

要解码图像,请使用 pylibdmtx 代替 .它输出一个坐标字典,我可以将其绘制在图像上并得到以下输出:decode_with_region()decode()

然后,我可以使用这些坐标来获得旋转角度:

def get_data_from_matrix(image):
    decoded = decode_with_region(image)[0]
    topLeft, topRight = decoded.rect[2], decoded.rect[3]
    rotation = -math.atan2(topLeft[1] - topRight[1], topLeft[0] - topRight[0]) * (180 / math.pi)
    image = image.rotate(rotation, expand=True)

评论

1赞 flakes 8/5/2023
很好,很高兴能帮上忙!在深入研究这样的使用问题时,总是值得在上游项目中进行搜索。对于其他任何与此问题作斗争的人,可能值得添加一个关于 Github 问题的这篇文章的链接!