0

I want to allow the user to adjust an image's contrast by a slider in my kivy app. Because I want to modify and update the current image real time. I am struggling to implement in Kivy.

From researching the problem, I think I should be using StringIO and / or ImageLoaderPyGame (these resources listed at the bottom of this post). The problem is I am confused about how to tie this to my kv file, and how to properly implement the StringIO and ImageLoaderPyGame with my current image. Some pseudo code describing what I'm trying to do is:

Slider :
    id: contrast
    value: 100
    min: 0
    max: 199
    on_value: root.update_contrast(contrast.value)

Image :
    id: displayed_image
    source: root.display_path

Python Code:

def update_contrast(self, value):
        buff = StringIO(self.display_image)
        buff.seek(0)
        img = ImageLoaderPygame(buff)
        self.display_image = self.change_contrast(img, value)

Which gives the following error:

buff = StringIO(self.display_image)
TypeError: initial_value must be str or None, not numpy.ndarray

Which I understand means I need to initiate the StringIO object with either None or a string, but I am confused about how to incorporate the current display image (which is defined through a file path variable - which I also cannot use to instantiate the StringIO, I get the still understandable error- "path should be string, bytes, os.PathLike or integer, not _io.StringIO")

Summary Currently, I have a display image object, defined through a user specified file path (which the kv image tag needs to display the image). I do not know how to allow the user to efficiently adjust the contrast of the display image. I think I need to use StringIO and ImageLoaderPyGame, but I do not see how to tie a file path and StringIO/ImageLoaderPyGame together.

Note: change_contrast is similar to the method suggested here: Change contrast of image in PIL

def change_contrast(img, level):
    factor = (259 * (level + 255)) / (255 * (259 - level))
    def contrast(c):
        return 128 + factor * (c - 128)
    return img.point(contrast)

Tried

mdoc-2011
  • 2,747
  • 4
  • 21
  • 43
  • What doesn't work about your suggested method? – inclement Dec 23 '17 at 01:53
  • I've updated the description of the problem above. Ultimately, I do not see how to use the StringIO/ImageLoaderPyGame with my app's displayed image, which is defined through a filepath. – mdoc-2011 Dec 23 '17 at 02:55

1 Answers1

0

Python Kivy is heavily lagging on Image Manipulations, mainly because of multiple critical bugs & bad solutions.
It is not created for such tasks, and should not be used for creating something like Image Editor.
So, I came up with this (dirty) solution.

from kivy.app import App
from kivy.uix.widget import Widget
from PIL import Image

class PongGame(Widget):
    c_image_path = '1.png'

    def update_contrast(self, cvalue):
        c_image = Image.open(self.c_image_path)
        self.change_contrast(c_image, self.ids.contrast.value)
        c_image.save('temp_' + self.c_image_path, 'PNG');

        self.ids.displayed_image.source = 'temp_' + self.c_image_path

    def change_contrast(self, img, level):
        factor = (259 * (level + 255)) / (255 * (259 - level))
        def contrast(c):
            return 128 + factor * (c - 128)
        return img.point(contrast)

class PongApp(App):
    def build(self):
        game = PongGame()
        return game


if __name__ == '__main__':
    PongApp().run()

Tried to implement in-memory operations, but ImageLoaderPIL is very glitchy, as well as all other such compatibility libraries.
p.s. Also you may see this

Abraham Tugalov
  • 1,902
  • 18
  • 25
  • There's no reason you couldn't make an image editor in Kivy that perfomed just fine. – inclement Dec 24 '17 at 00:02
  • Yes, but it's not a good choice for this task. Simple proof: There are no examples of image manipulations in Kivy documentation (other than Scatter). – Abraham Tugalov Dec 24 '17 at 09:59