How to access raw image data in kivy? - python

How to access raw image data of a kivy.core.image.Image object? The docs say that the image property of the Image object has the raw data of the image, but when I print it, it returns None:
This is what I did:
#paintwg is the widget
img = paintwg.export_to_image()
print(img.image)
Output:
>>> None

From kivy.uix.widget.Widget.export_as_image:
Return an core Image of the actual widget.
From kivy.core.image:
Core classes for loading images and converting them to a Texture. The raw image data can be keep in memory for further access.
The latter might relate to the kwarg keep_data. From the constructor kivy.core.image.Image(arg, **kwargs):
arg: can be a string (str), Texture, BytesIO or Image object
Let's check these four possibilitìes:
from kivy.app import App
from kivy.core.image import Image as Core_Image
from kivy.core.window import Window
from PIL import Image as PILImage
from io import BytesIO
class MyApp(App):
def build(self):
img_path = 'path/to/your/image.png'
# From string path
img = Core_Image(img_path)
print(img.image) # <kivy.core.image.img_sdl2.ImageLoaderSDL2 object at ...
# From Texture object
img = Core_Image(img.texture)
print(img.image) # None
# From BytesIO object
pil_img = PILImage.open(img_path)
img_bytes = BytesIO()
pil_img.save(img_bytes, format='PNG')
img_bytes.seek(0)
img = Core_Image(BytesIO(img_bytes.read()), ext='png')
print(img.image) # <kivy.core.image.img_sdl2.ImageLoaderSDL2 object at ...
# From existing image object
img = Core_Image(img_path)
img = Core_Image(img)
print(img.image) # <kivy.core.image.img_sdl2.ImageLoaderSDL2 object at ...
myapp = MyApp().run()
Even setting the kwarg keep_data=True doesn't store anything in the image property when using some Texture object as source.
Now – guess what! – how does export_as_image generate the image object? Correct:
img = Image(fbo.texture)
So, re-reading the second link, we know, that everything is stored in the corresponding texture property! Let's see (code taken from this Q&A):
from kivy.app import App
from kivy.uix.image import Image
from kivy.clock import Clock
class MyApp(App):
def build(self):
self.my_image = Image(source='path/to/your/image.png')
Clock.schedule_once(self.export, 1)
return self.my_image
def export(self, dt):
img = self.my_image.export_as_image()
print(img.image) # None
print(img.texture) # <Texture ... size=(800, 600) colorfmt='rgba' ...
myapp = MyApp().run()
In the pixels property of the Texture object, the raw data is stored. If you want to have that somehow "readable", e.g. using some Pillow Image object, you'd need to convert:
from kivy.app import App
from kivy.uix.image import Image
from kivy.clock import Clock
from PIL import Image as PILImage
class MyApp(App):
def build(self):
self.my_image = Image(source='path/to/your/image.png')
Clock.schedule_once(self.export, 1)
return self.my_image
def export(self, dt):
img = self.my_image.export_as_image()
pil_img = PILImage.frombytes('RGBA',
img.texture.size,
img.texture.pixels)
print(pil_img) # <PIL.Image.Image image mode=RGBA size=800x600 at ...
myapp = MyApp().run()
Now, you should be able to access single pixel values from the result of export_as_image during runtime!
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.9.1
PyCharm: 2021.1.1
kivy: 2.0.0
Pillow: 8.2.0
----------------------------------------

Related

How to access kivy image(widget) without kv/id

this is my first post in stackoverflow, be patient :D
I want to build a little picture editor with python/kivy.
It is my first time using kivy. I got some experience in python.
I don´t know how to update/reload an added image (widget). For example if I want to setup the contrast of a picture, the picture needs to be reloaded. I want to avoid saving pictures to disk, thats why I use BytesIO -> speed.
My solution right now is to delete the old image(widget) and add a new one. But I get in trouble if want to add other widgets, like buttons. So right now the code only works with one widget :-/
I tried also with kv language, but while I use BytesIO for processing my Image I cant use the kv/id function. Source only accepts string (path/files). But I got ByteArray.
Please take a look at my code, may you can show me how to access an added widget and update it later on in the running app. Or maybe there is a solution with kv and BytesIO?
Most of the code is gathered from web and put together...
from kivy.app import App
from kivy.uix.image import Image as UixImage
import rawpy
import imageio
from PIL import Image, ExifTags, ImageEnhance
import colorcorrect.algorithm as cca
from colorcorrect.util import from_pil, to_pil
from kivy.core.image import Image as CoreImage
import io
from kivy.clock import Clock
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
import kivy.uix.button as btn
import time
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import NumericProperty, ObjectProperty
# used .raf (Fujifilm Raw) in this project
# https://filesamples.com/samples/image/raf/sample1.raf
class MyImageWidget(GridLayout):
def __init__(self,**kwargs):
super(MyImageWidget,self).__init__(**kwargs)
self.cols = 1 #1 column grid layout
self.contrast_counter = 0 # counter for contrast sweep
# Load Image (+standard features)
self.thumbnail_path = self.create_thumbnailpath() # set path to thumbnail (create thumbnail)
self.image = Image.open(self.thumbnail_path) # open thumbnail
self.image = self.set_size(self.image, 0.1) # minimize thumbail size for speed
self.image = self.whitebalance_auto(self.image) # do whitebalance on thumbnail
self.image = self.set_greyscale(self.image) # do black white conversion
# add Image to widget (kivy_image)
self.kivy_image = UixImage(source=self.thumbnail_path)
self.add_widget(self.kivy_image) # <--- how can i access this widget later?
# here a object is creatd for the widget, but how do I access it later on?
print(self.children)
# TEST: reload Image in cycles (to see changing contrast effect on picture)
Clock.schedule_interval(self.update_pic,0.025) # refreshrate of app (25ms)
def update_pic(self, dt):
new_image = self.process_image(self.image)
self.remove_widget(self.kivy_image) # current widget need to be removed, so that contrast sweep is shown
self.kivy_image = new_image
self.add_widget(self.kivy_image) # new processed widget need to be added, so that contrast sweep is shown
#self.kivy_image.reload() # <--- how can i reload the widget (kivy_image), to not need to remove the old widget?
#print("reloaded")
def create_thumbnailpath(self):
# https://filesamples.com/samples/image/raf/sample1.raf
path = 'lab2raw/sample1.raf'
with rawpy.imread(path) as raw_image:
thumbnail_path = self.create_thumbnail(raw_image)
return thumbnail_path
def process_image(self,image):
self.contrast_counter = self.contrast_counter + 0.02 # cycle through contrast levels 0.0 - 2.0
if self.contrast_counter >= 2:
self.contrast_counter = 0 # reset contrast level cycle
#image = self.set_size(image, 0.2)
#image = self.whitebalance_auto(image)
#print_exif(image)
#image = self.set_greyscale(image)
#image = self.set_contrast(image,1.1)
image = self.set_contrast(image,self.contrast_counter)
#image = self.set_sharpness(image, 2.0)
image_processed = self.create_kivy_image(image, 'png')
return image_processed
def create_kivy_image(self,image, file_extension):
imgByteArr = io.BytesIO()
image.save(imgByteArr, file_extension)
# Return a Kivy image set from a bytes variable
imgByteArr.seek(0)
buf = io.BytesIO(imgByteArr.read())
buf.seek(0)
cim = CoreImage(buf, ext=file_extension)
image_to_show = UixImage(source='')
image_to_show.texture = cim.texture
return image_to_show
def whitebalance_auto(self,im):
image = to_pil(cca.retinex_with_adjust(from_pil(im)))
return image
def create_thumbnail(self,raw_image):
# Create JPG Thumbnail
try:
thumb = raw_image.extract_thumb()
except rawpy.LibRawNoThumbnailError:
print('no thumbnail found')
except rawpy.LibRawUnsupportedThumbnailError:
print('unsupported thumbnail')
else:
if thumb.format == rawpy.ThumbFormat.JPEG:
with open('lab2raw/thumb.jpg', 'wb') as file:
file.write(thumb.data)
return 'lab2raw/thumb.jpg'
elif thumb.format == rawpy.ThumbFormat.BITMAP:
imageio.imsave('lab2raw/thumb.tiff', thumb.data)
return 'lab2raw/thumb.tiff'
def print_exif(self,image):
img_exif = image.getexif()
if img_exif is None:
print('Sorry, image has no exif data.')
else:
for key, val in img_exif.items():
if key in ExifTags.TAGS:
if ExifTags.TAGS[key]=="XMLPacket":
pass
elif ExifTags.TAGS[key]=="PrintImageMatching":
pass
else:
print(f'{ExifTags.TAGS[key]}:{val}')
def set_greyscale (self,im):
image = im.convert("L")
return image
def set_contrast (self,im, contrast_level):
enhancer = ImageEnhance.Contrast(im)
return_image = enhancer.enhance(contrast_level)
return return_image
def set_sharpness (self,im, sharpness_level):
enhancer = ImageEnhance.Sharpness(im)
image = enhancer.enhance(sharpness_level)
return image
def set_size(self,im, size_factor):
width, height = im.size
im = im.resize((round(width*size_factor),round(height*size_factor)),Image.ANTIALIAS)
return im
def save_jpg(self,im,quality, optimize, path):
im.save(path,optimize=optimize, quality=quality)
class MyApp(App):
def build(self):
return MyImageWidget()
if __name__ == "__main__":
MyApp().run()
You don't have to delete self.kivy_image and create it again but you have to replace .texture instead of full Image
self.kivy_image.texture = new_image.texture
And this means you can also reduce code in create_kivy_image. You can return CoreImage instead of UixImage. You may also use one io.BufferIO
def create_kivy_image(self, image, file_extension):
buf = io.BytesIO()
image.save(buf, file_extension)
buf.seek(0)
return CoreImage(buf, ext=file_extension)
def update_pic(self, dt):
new_image = self.process_image(self.image)
self.kivy_image.texture = new_image.texture
You could also use io.BytesIo() to create thumbnail and you wouldn't have to write in file. I could test it only with JPG but I don't know if it is correct for BITMAP
def raw_to_pil(self, path):
"""Load RAW and convert to PIL.Image."""
with rawpy.imread(path) as raw_image:
try:
thumb = raw_image.extract_thumb()
except rawpy.LibRawNoThumbnailError:
print('no thumbnail found')
except rawpy.LibRawUnsupportedThumbnailError:
print('unsupported thumbnail')
else:
if thumb.format == rawpy.ThumbFormat.JPEG:
print('thumbnail JPG')
return Image.open(io.BytesIO(thumb.data))
elif thumb.format == rawpy.ThumbFormat.BITMAP:
print('thumbnail BITMAP')
buf = io.BytesIO()
imageio.imsave(buf, thumb.data, 'tiff')
buf.seek(0)
return Image.open(buf)
My full code for tests:
import io
import time
import rawpy
import imageio
from PIL import Image, ExifTags, ImageEnhance
import colorcorrect.algorithm as cca
from colorcorrect.util import from_pil, to_pil
from kivy.app import App
from kivy.clock import Clock
from kivy.core.image import Image as CoreImage
from kivy.uix.image import Image as UixImage
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.properties import NumericProperty, ObjectProperty
# used .raf (Fujifilm Raw) in this project
# https://filesamples.com/samples/image/raf/sample1.raf
class MyImageWidget(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 1 # 1 column grid layout
self.contrast_counter = 0 # counter for contrast sweep
# add Image to widget (kivy_image)
self.kivy_image = UixImage() # empty image
self.add_widget(self.kivy_image)
self.image = self.raw_to_pil('lab2raw/sample1.raf')
if self.image: # can be `None`
self.image = self.set_size(self.image, 0.1) # minimize thumbail size for speed
#self.image = self.whitebalance_auto(self.image) # do whitebalance on thumbnail
self.image = self.set_greyscale(self.image) # do black white conversion
core_image = self.pil_to_kivy(self.image, 'png')
self.kivy_image.texture = core_image.texture
# TEST: reload Image in cycles (to see changing contrast effect on picture)
Clock.schedule_interval(self.update_pic, 0.025) # refreshrate of app (25ms)
def update_pic(self, dt):
if self.image: # can be `None`
pil_image = self.process_image(self.image)
core_image = self.pil_to_kivy(pil_image, 'png')
self.kivy_image.texture = core_image.texture
def process_image(self, image):
"""Process PIL.Image without converting to other formats."""
self.contrast_counter += 0.02 # cycle through contrast levels 0.0 - 2.0
if self.contrast_counter >= 2:
self.contrast_counter = 0 # reset contrast level cycle
#image = self.set_size(image, 0.2)
#image = self.whitebalance_auto(image)
#image = self.set_greyscale(image)
#image = self.set_contrast(image, 1.1)
image = self.set_contrast(image, self.contrast_counter)
#image = self.set_sharpness(image, 2.0)
#self.print_exif(image)
return image
def pil_to_kivy(self, image, file_extension):
"""Convert PIL.Image to CoreImage (to get later .texture)."""
buf = io.BytesIO()
image.save(buf, file_extension)
buf.seek(0)
return CoreImage(buf, ext=file_extension)
def raw_to_pil(self, path):
"""Load RAW and convert to PIL.Image. Can return `None`."""
with rawpy.imread(path) as raw_image:
try:
thumb = raw_image.extract_thumb()
except rawpy.LibRawNoThumbnailError:
print('no thumbnail found')
except rawpy.LibRawUnsupportedThumbnailError:
print('unsupported thumbnail')
else:
if thumb.format == rawpy.ThumbFormat.JPEG:
print('thumbnail JPG')
buf = io.BytesIO(thumb.data)
return Image.open(buf)
elif thumb.format == rawpy.ThumbFormat.BITMAP:
print('thumbnail BITMAP')
buf = io.BytesIO()
imageio.imsave(buf, thumb.data, 'tiff')
buf.seek(0)
return Image.open(buf)
#return Image.new('RGB', (1500, 1500), 'white') # create empty Image
def print_exif(self, im):
im_exif = im.getexif()
if im_exif is None:
print('Sorry, image has no exif data.')
else:
for key, val in im_exif.items():
if key in ExifTags.TAGS:
if ExifTags.TAGS[key] == "XMLPacket":
print('pass (XMLPacket)')
pass
elif ExifTags.TAGS[key] == "PrintImageMatching":
print('pass (PrintImageMatching)')
pass
else:
print(f'{ExifTags.TAGS[key]}:{val}')
# ---
def whitebalance_auto(self, im):
return to_pil(cca.retinex_with_adjust(from_pil(im)))
def set_greyscale(self, im):
return im.convert("L")
def set_contrast(self, im, contrast_level):
return ImageEnhance.Contrast(im).enhance(contrast_level)
def set_sharpness(self, im, sharpness_level):
return ImageEnhance.Sharpness(im).enhance(sharpness_level)
def set_size(self, im, factor):
return im.resize((round(im.width*factor), round(im.height*factor)), Image.ANTIALIAS)
def save_jpg(self, im, quality, optimize, path):
im.save(path, optimize=optimize, quality=quality)
class MyApp(App):
def build(self):
return MyImageWidget()
if __name__ == "__main__":
MyApp().run()

Create Enum referencing a list with TkImages

I've got a couple of images in a folder and each of these gets resized and turned into an image for Tkinter. I want to use these images in other python scripts as a enum.
This is how I create the images:
import os
from PIL import Image, ImageTk
from enum import Enum
#Resize All Images and turn them into TkImages
def ResizeImages():
PieceImages = []
for images in os.listdir('Resources'):
cImage = Image.open('Resources/' + images)
cImage = cImage.resize((80, 80), Image.ANTIALIAS)
PieceImages.append(ImageTk.PhotoImage(cImage))
return PieceImages
#Called from outside to create PNGS list AFTER Tkinter.Tk() was called
def InitializeImages():
global PNGS
PNGS = ResizeImages()
#This is the part where I'm lost
class Black(Enum):
global PNGS
Queen = PNGS[3]
King = PNGS[4]
Tower = PNGS[5]
I want to create a Tkinter Button with these images on like this:
#PieceImages is the .py file above
import PieceImages import Black
form tkinter import *
root = Tk()
root.geometry('800x800')
someButton = tk.Button(root, image=Black.Tower).place(x=100, y=100)
My Question is:
Is it even possible to create enums of this type and if so, how?
I'd recommend staying away from global variables; you can pass arguments to a class directly:
class Black():
def __init__(self, pngs):
self.name1 = pngs[0]
self.name2 = pngs[1]
Instantiate by passing pngs:
black = Black(pngs)
Then black.name1, black.name2 etc will be your desired images.

Add color filter to Tk.Label

I have a tk.Label object with an image (via tk.Label(self.root,image='image.png')) and want to add a color filter to it, i.e. make the image more red. How can I do that?
You can modify the image with PIL, and save it as a new file. In Tkinter, you can use PhotoImage(file="file.png") to use the image as a button:
# importing image object from PIL
from PIL import ImageOps, Image
import PIL.Image
# creating an image object
img = Image.open(r"test.png").convert("L")
# image colorize function
img = ImageOps.colorize(img, black="red", white="white")
# save the image as new.png
img.save("new.png")
from tkinter import *
root = Tk()
# set the img1 variable as the image we just created
img1 = PhotoImage(file="new.png")
b = Button(image=img1)
b.pack()
root.mainloop()

how Write a text on png image Using python PIL

I am trying to code for make a registration System and Identity Card printer first form done with making data import to database using sqlite3 database and tkinter GUI.this form for get data from database and write that data text on png image.that png image after written data it is the identity card.
**so this is the problem..code run without any errors.but image not written data.for try this code firstly i didn't use database data.i used data from string variable **
import sqlite3
from tkinter import Tk, Button, Canvas
from PIL import Image, ImageFont, ImageDraw
connection = sqlite3.connect("school.db")
tao = Tk()
tao.title("Mayurapada Central Collage")
tao.configure(bg = '#6699ff')
canvas = Canvas(tao,width = 600,height = 400,bg = '#6699ff')
def imgs():
img = Image.open("C:\\Users\\TAO\\Desktop\\New\\02.png")
#img.show()
str01 = "Hello World"
font = ImageFont.truetype("arial.ttf",200)
w,h = font.getsize(str01)
print(str01)
draw = ImageDraw.Draw(img)
draw.text(((900-w)/2,(900-h)/2),str01,font = font,fill = "black")
img.show()
button01 = Button(tao,text = "Preview",bd = 7,padx = 5,pady = 5,command =
imgs).place(x = 50,y = 300)
canvas.pack()
tao.mainloop()

How to use BytesIO with matplotlib and pyqt5?

I made a graph in matplotlib, and wanted to make it in to an image and use it in my pyqt5 application. Someone suggested I use BytesIO for this. This is my code so far:
Drawing my graph:
...
plt.axis('equal')
buff = io.BytesIO()
plt.savefig(buff, format="png")
print(buff)
return buff
This is then called in another script:
def minionRatioGraphSetup(self, recentMinionRatioAvg):
image = minionRatioGraph(recentMinionRatioAvg)
label = QtWidgets.QLabel()
pixmap = QtGui.QPixmap(image)
label.setPixmap(pixmap)
label.setGeometry(QtCore.QRect(0,0,200,200))
It stops working at pixmap = QtGui.QPixmap(image) and I'm unsure why. Also: How could I place this in my MainWindow? because I doubt the code there will work lol
I'm sure there is a solution using a buffer. However, it seems rather complicated to get the byte format correct. So an alternative is to save the image to disk, and then load it from there.
import sys
from PyQt4 import QtGui
import matplotlib.pyplot as plt
import numpy as np
def minionRatioGraph():
plt.plot([1,3,2])
plt.savefig(__file__+".png", format="png")
class App(QtGui.QWidget):
def __init__(self):
super(App, self).__init__()
self.setGeometry(300, 300, 250, 150)
self.setLayout(QtGui.QVBoxLayout())
label = QtGui.QLabel()
label2 = QtGui.QLabel("Some other text label")
minionRatioGraph()
qimg = QtGui.QImage(__file__+".png")
pixmap = QtGui.QPixmap(qimg)
label.setPixmap(pixmap)
self.layout().addWidget(label)
self.layout().addWidget(label2)
self.show()
if __name__ == '__main__':
app = QtGui.QApplication([])
ex = App()
sys.exit(app.exec_())
a snippet using pillow, might help avoiding file io
im = PIL.Image.open("filename")
with BytesIO() as f:
im.save(f, format='png')
f.seek(0)
image_data = f.read()
qimg = QImage.fromData(image_data)
patch_qt = QPixmap.fromImage(qimg)
Here's a solution that only uses the buffer, you need to use PIL to create an ImageQT and then load it to the QPixap
import matplotlib.pyplot as plt
import io
from PIL.ImageQt import ImageQt
from PIL import Image
...
buff = io.BytesIO()
plt.savefig(buff, format="png")
img = Image.open(buff)
img_qt = ImageQt(img)
return img_qt
Then, in your GUI setup, you call your function to return the ImageQT and generate the QPixmap using QPixmap.fromImage()
def minionRatioGraphSetup(self, recentMinionRatioAvg):
image = minionRatioGraph(recentMinionRatioAvg)
label = QtWidgets.QLabel()
pixmap = QtGui.QPixmap.fromImage(image)
label.setPixmap(pixmap)
label.setGeometry(QtCore.QRect(0,0,200,200))

Categories

Resources