[英]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格式),宽度为奇数561
即data.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.
如果真的很难放大,则可以在左下角看到一些压缩伪像,例如压缩伪像。
Those artifacts mutliplied by the aforementioned random color components rotation bug give you very strange (seemingly non-existent) colors. 由上述随机颜色分量旋转错误引起的杂色给您带来非常奇怪(看似不存在)的颜色。
Possible workarounds 可能的解决方法
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.