简体   繁体   English

为什么kivy read_pixel不返回预期的颜色?

[英]Why is kivy read_pixel not returning expected color?

I am trying to create markers on an image that would allow a user to select colors, mark features etc. Ultimately I would like to have the corresponding image pixel for further use via opencv. 我正在尝试在图像上创建标记,以允许用户选择颜色,标记特征等。最终,我希望具有相应的图像像素,以便通过opencv进一步使用。

I'm having a lot of trouble getting the expected color under the touch and it sometimes returns colors like magenta which are not even in the example image. 我很难在触摸下获得预期的颜色,有时返回的颜色像是洋红色,即使在示例图像中也没有。

I am pretty sure the problem is with how I am converting the touch position to the values I am handing to the read_pixel function. 我很确定问题出在如何将触摸位置转换为我要提供给read_pixel函数的值。

I have tried many different solutions without success so I think there is something I am missing here. 我尝试了许多不同的解决方案但均未成功,因此我认为这里缺少一些东西。

main.py main.py

from kivy.app import App
from kivy.properties import ListProperty, ObjectProperty
from kivy.uix.image import AsyncImage
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import ScreenManager, Screen


class Marker(Widget):
    selected_color = ListProperty([0,1,0])

    def __init__(self, **kwargs):
        super(Marker, self).__init__(**kwargs)
        self.selected_pos = None

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            print("Touched at Marker: {0}".format(touch.spos))

    def on_touch_move(self, touch):
        self.set_position_from_touch(touch.spos)

    def set_position_from_touch(self, spos):
        # print("touch: {0}".format(touch))
        self.image = self.parent.parent.image
        x = spos[0] * self.image.width
        y = spos[1] * self.image.height

        # setting position of the widget relative to touch
        self.pos = (x-self.width/2, y-self.height*(2/3))
        # self.pos = (x, y)

        print("widget position : {0}".format(self.pos))
        # converting widget position to pixel(row, column of
        selected_pixel = self.image.to_row_col(self.pos)
        print("selected Pixel: {0}".format(selected_pixel))

        try:
            self.selected_color = self.image._coreimage.read_pixel(
                selected_pixel[0],
                selected_pixel[1])
                # this skips conversion and just uses pos
                # self.pos[0],
                # self.pos[1])
        except IndexError:
            print("position out of range")


class MarkerManager(RelativeLayout):
    def __init__(self, **kwargs):
        super(MarkerManager, self).__init__(**kwargs)
        self.marker_mode = None
        self.features = []

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            child_touched = False
            print("Touched: {0}".format(touch))
            if self.children:
                for child in self.children[:]:
                    if child.collide_point(touch.pos[0], touch.pos[1]):
                        child_touched = True
                        child.dispatch('on_touch_down', touch)
            if not child_touched:
                print("Touched only Image at: {0}".format(touch.spos))
                marker = Marker()
                self.features.append(marker)
                self.add_widget(marker)
                marker.set_position_from_touch(touch.spos)

class SelectedImage(AsyncImage):
    def __init__(self, **kwargs):
        super(SelectedImage, self).__init__(**kwargs)
        self.allow_stretch=True
        self.keep_ratio=False

    def to_row_col(self, pos):
        pixels_x = self._coreimage.width
        pixels_y = self._coreimage.height
        pixel_x = (pos[0] / self.width) * pixels_x
        # pixel_y = (pos[1] / self.height) * self.pixels_y
        pixel_y = (1 - (pos[1] / self.height)) * pixels_y
        # should correspond to row column of image
        return [int(pixel_x), int(pixel_y)]

class ImageScreen(Screen):
    image = ObjectProperty()
    manager = ObjectProperty()
    def __init__(self, **kwargs):
        super(ImageScreen, self).__init__(**kwargs)

class PointsSelectorApp(App):
    def build(self):
        return ImageScreen()

if __name__ == "__main__":
    PointsSelectorApp().run()

pointsselector.kv pointsselector.kv

<ImageScreen>:
    image: image_id
    manager: manager_id

    SelectedImage:
        id: image_id
        source: "rainbow_checkerboard.jpg"
        keep_data: True

    MarkerManager:
        id: manager_id


<Marker>:
    size_hint: None, None
    size: "40dp", "40dp"
    canvas:
        Color:
            rgb: self.selected_color
        Ellipse:
            pos: self.pos[0]+self.width/4, self.pos[1]+self.height/3
#            pos: self.pos[0], self.pos[1]
            size: self.width*.6, self.height*.6

here is my image i have been using to test with "rainbow_checkerboard.jpg" 这是我用来测试“ rainbow_checkerboard.jpg”的图片

我的测试图像

I believe this is a bug in the Kivy itself. 我相信这是Kivy本身的错误。 Particularly I think that line 901 at the kivy/core/image/__init__.py 特别是我认为kivy / core / image / __ init__.py的第901行

    index = y * data.width * size + x * size
    raw = bytearray(data.data[index:index + size])
    color = [c / 255.0 for c in raw]

is wrong. 是错的。 It should be 它应该是

    index = y * data.rowlength + x * size

instead. 代替。

Important thing here is that for performance reasons bitmap images in memory are alligned at 4 bytes addresses. 此处重要的是,出于性能方面的考虑,内存中的位图图像分配在4个字节的地址上。 Thus there is an explicit data.rowlength field. 因此,存在一个显式的data.rowlength字段。 Usually that line works fine because images are typically well alligned so that data.rowlength = data.width * size . 通常,这行效果很好,因为通常会很好地data.rowlength = data.width * size图像,因此data.rowlength = data.width * size But your particular image is different: it uses 3-bytes RGB format (no alpha) and its width is odd 561 ie data.width * size = 1683 and so data.rowlength should be rounded up to 1684 , which it actually is but that code doesn't take it into account. 但是您的特定图像有所不同:它使用3字节RGB格式(无alpha格式),宽度为奇数561data.width * size = 1683 ,因此data.rowlength应该四舍五入为1684 ,但实际上是代码没有考虑到它。 It means that often you read colors from two consecutive pixels and with RGB components randomly rotated. 这意味着您经常从两个连续的像素中读取颜色,并随机旋转RGB分量。

Additionally since you use JPEG format for your image, borders between "cells" are not really strict. 另外,由于您对图像使用JPEG格式,因此“单元格”之间的边界并不是很严格。 If you zoom in really hard, you can see some compression artifacts such as these from around bottom-left corner. 如果真的很难放大,则可以在左下角看到一些压缩伪像,例如压缩伪像。

JPEG工件

Those artifacts mutliplied by the aforementioned random color components rotation bug give you very strange (seemingly non-existent) colors. 由上述随机颜色分量旋转错误引起的杂色给您带来非常奇怪(看似不存在)的颜色。

Possible workarounds 可能的解决方法

  1. Change your image so that it is pre-aligned in the memory and the bug doesn't affect you (for example use mutliple of 4 as width) 更改图像,使其在内存中预先对齐,并且该错误不会影响您(例如,使用4的mutliple作为宽度)
  2. Submit a bug or even a pull request to the Kivy and wait for it to be fixed 向Kivy提交错误或什至请求请求,然后等待其修复
  3. Patch kivy/core/image/__init__.py file locally (I tried it and it seems to work fine). 在本地修补kivy/core/image/__init__.py文件(我尝试过,它似乎工作正常)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM