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

huangapple go评论81阅读模式
英文:

Use data matrix as a fiducial to obtain angle of rotation

问题

<img src="https://i.stack.imgur.com/ubm2b.png" width="200">
我有一堆像上面那样的图像它们每个都包含一个数据矩阵但不能保证它们是朝向轴的尽管如此我可以使用`libdmtx`相当可靠地读取这些矩阵而不管它们的旋转如何但是我还需要旋转图像以便标签朝上我的思路是我需要获取数据矩阵的旋转角度以便我可以使用PIL将图像旋转到正确的方向。`pylibdmtx.decode`返回矩阵包含的数据以及一个矩形最初我以为它是数据矩阵的边界框为了测试这一点我使用上面的图像运行了以下代码

```python
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)。由于高度是负数,我认为这不是数据矩阵的边界框,如果是的话,我不知道如何使用它来获取旋转角度。

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

我对任何建议都持开放态度。提前感谢。


<details>
<summary>英文:</summary>

&lt;img src=&quot;https://i.stack.imgur.com/ubm2b.png&quot; width=&quot;200&quot;&gt;

I have a bunch of images such as the one above. They each contain a data matrix, but do not guarantee that it is oriented to an axis. Nevertheless, I can read these matrices with `libdmtx` pretty reliably regardless of their rotation. However, I also need to rotate the image so that the label is oriented right-side-up. My thought process is that I need to get the angle of rotation of the data matrix so that I can rotate the image with PIL to orient it correctly. `pylibdmtx.decode` returns the data that the matrix contains, as well as a rectangle which I originally thought was the bounding box of the data matrix. To test this, I ran the following code with the image above:

```python
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__ == &quot;__main__&quot;:
    segment_qr_code(Image.open(&#39;&lt;path to image&gt;&#39;))

Unfortunately, this code returned Rect(left=208, top=112, width=94, height=-9). Because the height is negative, I don't think it is the bounding box to the data matrix, and if it is, I don't know how to use it to get the angle of rotation.

My question is, what is the best way to obtain the angle of rotation of the data matrix? I originally thought that I could crop the image with the bounding box to get a segmented image of just the data matrix. Then I could use image thresholding or contouring to get an angle of rotation. However, I'm not sure how to get the correct bounding box, and even if I did I don't know how to use thresholding. I would also prefer to not use thresholding because it isn't always accurate. The data matrix always has a solid border on the bottom and left sides, so I think it may be possible to use it as a fiducial to align the image, however I was unable to find any libraries that were able to return the angle of rotation of the data matrix.

I am open to any suggestions. Thanks in advance.

答案1

得分: 3

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

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

要解码图像,请使用decode_with_region()而不是pylibdmtx的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)
英文:

Thank you to @flakes for the suggestion. Combining code from the PR and issue, I created the following solution:

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(&#39;Decoded&#39;, &#39;data rect&#39;)


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[&#39;01&#39;][&#39;x&#39;], height - res.rect[&#39;01&#39;][&#39;y&#39;])
                            topRight = (res.rect[&#39;11&#39;][&#39;x&#39;], height - res.rect[&#39;11&#39;][&#39;y&#39;])
                            bottomRight = (res.rect[&#39;10&#39;][&#39;x&#39;], height - res.rect[&#39;10&#39;][&#39;y&#39;])
                            bottomLeft = (res.rect[&#39;00&#39;][&#39;x&#39;], height - res.rect[&#39;00&#39;][&#39;y&#39;])
                            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),
                {
                    &#39;00&#39;: {
                        &#39;x&#39;: int((vector00.X) + 0.5),
                        &#39;y&#39;: int((vector00.Y) + 0.5)
                    },
                    &#39;01&#39;: {
                        &#39;x&#39;: int((vector01.X) + 0.5),
                        &#39;y&#39;: int((vector01.Y) + 0.5)
                    },
                    &#39;10&#39;: {
                        &#39;x&#39;: int((vector10.X) + 0.5),
                        &#39;y&#39;: int((vector10.Y) + 0.5)
                    },
                    &#39;11&#39;: {
                        &#39;x&#39;: int((vector11.X) + 0.5),
                        &#39;y&#39;: int((vector11.Y) + 0.5)
                    }
                }
            )
        else:
            return None

To decode an image, use decode_with_region() instead of pylibdmtx's decode(). It outputs a dictionary of coordinates, which I can plot on an image and get the following output:

<img src="https://i.stack.imgur.com/xaytm.png" width="300">

I can then use these coordinates to obtain an angle of rotation:

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)

huangapple
  • 本文由 发表于 2023年7月27日 21:48:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76780411.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定