Problem in saving Python tkinter Canvas drawing as an image file using PIL Image and ImageDraw functions - python-3.x

Problem statement
I have written a code in Python on which a user can draw on a tkinter GUI canvas. The program is capable of taking the app's screenshot but saving the canvas content to a viewable image file (such as .jpg or .png) is not properly done yet.
Screenshots
What I draw on the tkinter app's canvas.
What the code (given below) saved.
What I expect.
I have tried and will expect
A number of possible solutions from the internet (including Stack Overflow) have been tried but somehow the process cannot be completed successfully. After drawing whatever on the canvas, the saved image will have nothing but a dot whether it is in *.jpg or *.png. I need the image of the full canvas with all the colorful drawings on it in that saved image file.
For any reference, here is my code
from tkinter import Tk, Canvas, Button
from PIL import Image, ImageDraw
X = 0
Y = 0
app = Tk()
def set_xy(click):
global X, Y
X, Y = click.x, click.y
def draw_line(drag):
global X, Y
draw_panel.create_line((X, Y, drag.x, drag.y), fill="black", capstyle="round", smooth=True)
draw.line([(X, Y), (drag.x, drag.y)], fill="black", joint="curve")
X, Y = drag.x, drag.y
def save(event=None):
# ATTENTION: This is not working properly
image.save("image.png", "png")
# App's drawing panel
draw_panel = Canvas(app, background="white", cursor="dot")
draw_panel.place(x=0, relwidth=1.0, y=0, relheight=1.0)
# Invisible canvas for saving as an image later on
image = Image.new("RGB", (draw_panel.winfo_width(), draw_panel.winfo_height()), "white")
draw = ImageDraw.Draw(image)
# Mouse and Key bindings
draw_panel.bind('<Button-1>', set_xy)
draw_panel.bind('<B1-Motion>', draw_line)
app.bind('<s>', save) # press 's' to save the drawing as .png
app.mainloop()
Addition Informations:
HOST: Windows 10 Version 20H2 (OS Build 19042.2130)
IDE: PyCharm 2022.2 (Community Edition) (Build #PC-222.3345.131)
Language: Python 3.10.6
Framework: tkinter Tk/Tcl 8.6
Library: pillow 9.2.0
Any solution shall be highly appreciated, and I shall be thankful.

That's a nice idea and there is just one piece missing. You set the size of the image to the size of the canvas before you call mainloop() for the first time. The canvas does not have a size at this time because the window has not been initiated, yet.
You need to force an update before getting the width and height. Then, it works fine:
# Invisible canvas for saving as an image later on
draw_panel.update()
image = Image.new("RGB", (draw_panel.winfo_width(), draw_panel.winfo_height()), "white")
draw = ImageDraw.Draw(image)
If you print the draw_panel.winfo_width() before and after calling the update, you see what I mean. Before update(), width and height are both 1. Afterwards, they are 200, as they should be.

Related

Is it possible to create mobile app in streamlit

I am trying to create mobile app in streamlit but I have no idea how to do that could someone help me to create streamlit mobile app by giving example
In mobile app we should take pictures and crop the same.
You will need to use some external libraries or frameworks that provide the necessary functionality for capturing and cropping images.
One option is to use the Python Pillow library, which provides functions for reading and manipulating image files. You can use the Image.open() function to open an image file, and the Image.crop() function to crop the image. You can then use Streamlit's st.image() function to display the cropped image in your app.
import streamlit as st
from PIL import Image
# Open an image file
image = Image.open("image.jpg")
# Crop the image
# The crop() function takes a tuple of (left, upper, right, lower) coordinates
# that define the bounding box for the cropped image.
cropped_image = image.crop((100, 100, 200, 200))
# Display the cropped image
st.image(cropped_image)

Camera stays on after object is destroyed, how to turn off the camera LED in OpenCV 4.1.2.30?

The LED of my camera is not turning off even when the process is finished. I've simply created a function to capture the image and then the camera must be turned off, but that's not happening.
I've even tried writing .release() function and .VideoCaptureRelease() function, but all went in vain.
The Python version I'm using is 3.6.9, on Linux (Ubuntu 18.04), on PyCharm IDE 19.3.2. On top of all openCV version is 4.1.2.30.
The problem didn't occur in openCV 4.1.0.25!
Anyhow, in the latest version of OpenCV, out of the blue, the LED is permanently on after using the camera. Here's the code of my small task:
from cv2 import *
import os
class Camera:
def capture_pic():
cam = VideoCapture(0)
s, img = cam.read()
if s:
namedWindow("cam-test", flags=WINDOW_AUTOSIZE)
imshow("cam-test", img)
waitKey(0)
destroyWindow("cam-test")
imwrite("test_pic.jpg", img) # save image
imshow('test_pic.jpg', img)
waitKey(0)
destroyAllWindows()
cam.release() # Used but no results
Camera.capture_pic()
Any suggestions or help would be appreciated.
Thanks in advance
This issue was first reported here and it seems to be caused by a problem in the MSMF capture backend.
Some people report that a temporary fix is to set the following environment variable to 0 before running the script:
export OPENCV_VIDEOIO_PRIORITY_MSMF=0
You could release the cam after your if statement and just after that enter in an infinite while loop to keep the openCV screen opened.
Additionally, You could add a conditional with a waitkey to break the loop and then close the window.
from cv2 import *
import os
class Camera:
def capture_pic():
cam = VideoCapture(0)
s, img = cam.read()
if s:
namedWindow("cam-test", flags=WINDOW_AUTOSIZE)
imshow("cam-test", img)
destroyWindow("cam-test")
imwrite("test_pic.jpg", img) # save image
cv2.imshow('test_pic.jpg', img)
cam.release() # release the cam just after showing your image.
while True:
if cv2.waitKey(1) & 0xFF == ord('q'):
destroyAllWindows()
break
Camera.capture_pic()

cv2.cvtColor(img,cv2.COLOR_BGR2RGB) not working

I am trying to create a screen recorder using mss and Opencv in python, the video I am capturing has a very different colours than original computer screen. I tried to find the solution online, Everyone saying it should be fixed using cvtColor() but I already have it in my code.
import cv2
from PIL import Image
import numpy as np
from mss import mss
import threading
from datetime import datetime
`
def thread_recording():
fourcc=cv2.VideoWriter_fourcc(*'mp4v')
#fourcc=cv2.VideoWriter_fourcc(*'XVID')
out=cv2.VideoWriter(vid_file,fourcc,50,(width,height))
mon = {"top": 0, "left": 0, "width":width, "height":height}
sct = mss()
thread1=threading.Thread(target=record,args=(mon,out,sct))
thread1.start()
def record(mon,out,sct):
global recording
recording=True
while recording:
frame= np.array(sct.grab(mon))
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
out.write(frame)
out.release()
the vid_file variable contains a string of output file name with mp4 extension
Screenshot of my screen
Screenshot from recorded video
So, I looked around some more and found that apparently this is a bug in opencv for versions 3.x on wards.then I tried PIL for getting rgb image and removed cvtColor(),but it produced an empty video.I removed both cvtColor() as well as PIL Image as suggested by #ZdaR it again wrote empty video Hence I had to put it back and boom. even if cvtColor() seems like doing nothing, for some unknown reason it has to be there.when you use PIL Image along with cvtColor() it writes the video as expected
from PIL import Image
def record(mon,out,sct):
global recording
recording=True
while recording:
frame=sct.grab(mon)
frame = Image.frombytes('RGB', frame.size, frame.rgb)
frame = cv2.cvtColor(np.array(frame), cv2.COLOR_BGR2RGB)
out.write(np.array(frame))
out.release()
as I am very new to programming, I would really appreciate your help if I missed or overlooked something important
You can do
frameRGB = cv2.cvtColor(frame,cv2.COLOR_RGB2BGR)
Frame is in BGR, and it will work the same as you are only changing R with B where frameRGB is in RGB now. This command will transfer R to B and works to transfer frames from RGB and BGR as well as BGR to RGB. BGR2RGB might be a bug, I have it as well but the command I mentioned works perfectly. That's what I do.
MSS store raw BGRA pixels. Does it work if you change to:
# Grab it
img = np.array(sct.grab(mon))
# Convert from BGRA to RGB
frame = cv2.cvtColor(img, cv2.COLOR_BGRA2RGB)
you should run this command in cmd
pip install opencv-python

How do i close a figure shown using matplotlib in python ? And What is difference among image, figure and picture?

I am using ubuntu. I want to close a figure shown using matplotlib after few seconds without using keyboard or mouse. I am able to close an image shown using PIL after few seconds by getting its process id and then kill it.
And i am also little bit confused among terms figure, image and picture in matplotlib.
Thank you so much in advance.
Regarding part 1.
i have used plt.close(), plt.close("all") as well as 'psutil' library to fetch process ID and kill. But none of them worked. I got only solution of closing an image opened via 'PIL'.
link :-
How can I close an image shown to the user with the Python Imaging Library?
Regarding part 2.
Actually, at some pages, i found the terms 'figure','picture' and 'image' were used interchangeably; and at some pages they were not. I saw 'plt.imshow()' is used for image and picture and 'plt.show()' is used for figure. But, what is difference between figure, image and picture. And when to use these functions?
link :-
Why plt.imshow() doesn't display the image?
# for graphing
import matplotlib.pyplot as plt
import time
# for process
import psutil
# importing for image processing
from PIL import Image
#### closing an image which was opened via PIL
#### working perfectly
filename = "check.jpg"
img = Image.open(filename)
img.show()
time.sleep(5)
# for killing process such that image viewer
for proc in psutil.process_iter():
if proc.name() == "display":
proc.kill()
#### closing an image/figure which was opened via matplotlib
#### unable to close without keyboard or mouse
x = [[1,2,3,4],[11,22,33,44],[9,8,7,6]]
print (x)
plt.imshow(x)
plt.colorbar()
plt.title("a")
plt.xlabel('b')
plt.ylabel('c')
a = plt.show()
time.sleep(2)
## not working
plt.close()
## not working
for proc in psutil.process_iter():
if proc.name() == "display":
proc.kill()
## not working
plt.close("all")
i expect that my shown figure closes automatically after a few seconds,
instead of any manual intervention.

Tkinter best button text flipping method

So I'm working on a tkinter project and one issue I come across is finding a way to flip/rotate a button object's text vertically. One way I can kinda cheat into making this happen is putting a canvas object on top of the button with the canvas being drawn last (as shown below) but is there a cleaner way to approach this by just manipulating the Button object attributes?
from tkinter import*
root = Tk()
windowDimensions = (1300,600)
root.title("Mapper")
root.geometry(str(windowDimensions[0])+"x"+str(windowDimensions[1]))
button1=Button(root,text='',width=2,height=9)
button1.place(x=0,y=20)
can = Canvas(root,width=15,height=80)
can.place(x=2,y=30)
can.create_text(0, 80, anchor="nw", angle=90,text='hello',font=("Purisa", 12))
root.mainloop()
Edit: A problem I get with doing it this way is any place where the canvas is on the button, it obstructs the ability to click where the canvas is.
Your best option (which isn't a great option) is to screenshot the button, rotate it in an image editor, and then use that image in your button instead of text.
from tkinter import*
root = Tk()
# .gif file encoded as base64
vert_button_data = '''
R0lGODlhDQBCAKUkAAAAAAAANgAAYDYAADYANjYAYGAAAGAANmAAYAA2hzY2h4c2AGA2h4c2NgBg
qzZghzZgq2BgNqtgAKtgNjaHhzaHzmCHh6uHNs6HNmCr8PCrYIfOq4fO8PDOh6vwq6vw8M7w8PDw
q/DwzvDw8P//////////////////////////////////////////////////////////////////
/////////////////////////////////////////////ywAAAAADQBCAAAG/sCRcEgsGo+cpFJJ
PDgPAWeUGKpSMFUM5Qggdo2DDAiU+RY7BoXCoDmOQp1OqLisN5/4A7XqqfqPAm5EERVLRwcGeEd+
f0gZGR9uGQQSEgUcXB1CHQFcXkdhHx8ZA0doBQUGmotxc0QOhZFuGhgNCLAcskUiIbQNAg5HHxwV
DwYSRMQQCBMYckUHAxYbIG5wtQwOGRzVRrwaFwEJRiAcGQ4KCxhE5woNEhodIkXw8oL3+Pn35pCS
lJaYjABYxcnTEDNEQo0qZeRUqlVG4DxLVofJkDyKhjDaSLFiQCEYn+zZWEVYhWAjUBYRoOELwoMj
XILiAOBDBYZnoj201oqOIMePI/BEEanRDwBGbl4avKdUyBIAhi4+SURUCMlG+oIAADs=
'''
windowDimensions = (1300,600)
root.title("Mapper")
root.geometry("{}x{}".format(*windowDimensions))
button1_image = PhotoImage(data=vert_button_data)
button1=Button(root,image=button1_image)
button1.place(x=0,y=20)
root.mainloop()
You'll lose the hover animation but again that's something you can recreate with images.
To get the base64 encoded data from a .gif you can use this:
import codecs
with open('export.gif', 'rb') as f:
print(codecs.encode(f.read(), 'base64').decode())

Resources