Convert Current Image to Black and White - python

The goal is to convert the current image in GUI window to black and white
Below is my code:
def BlackAndWhite(self):
from images import Image
LoadAFile = self.inputText.getText()
CurrentImage = open(LoadAFile)
image = self.image = PhotoImage(file = LoadAFile)
image.draw()
BlackAndWhite(image)
image.draw()
self.imageLabel["image"] = self.image
blackPixel = (0,0,0)
whitePixel = (255,255,255)
for y in range(image.getHeight()):
for x in range(image.getWidth()):
(r,g,b) = image.getPixel(x,y)
average = (r+b+g) /3
if average < 128:
image.setPixel(x,y,blackPixel)
else:
image.setPixel(x,y, whitePixel)
I am getting this error message:
image.draw()
AttributeError: 'PhotoImage' object has no attribute 'draw'

Here's working code, you should be able to tweak it to work with your work:
from tkinter import Tk, Canvas, NW
from PIL import ImageTk, Image
root = Tk()
canvas = Canvas(root, width=1000, height=1000)
canvas.pack()
img = Image.open("PATH_TO_AN_IMAGE")
blackPixel = (0, 0, 0)
whitePixel = (255, 255, 255)
for y in range(img.height):
for x in range(img.width):
pixelVal = img.getpixel((x, y))
# Unpacking in this way in case the pixel contains more than R, G, B (ex: a png)
r, g, b = pixelVal[0:3]
average = (r + b + g) / 3
if average < 128:
img.putpixel((x, y), blackPixel)
else:
img.putpixel((x, y), whitePixel)
photoimage = ImageTk.PhotoImage(img)
canvas.create_image((20, 20), anchor=NW, image=photoimage, state="normal")
root.mainloop()

Related

How can I display an image slideshow in tkinter that I made with OpenCv so that I still have 1 line or 3 grids for text?

I'm very new to programming pytho my 3rd month. I'm making a desktop program that is supposed to show appointments and sayings. I already have a function process that opens a window in full screen and displays the slide show.
But I want to place the slide show in a tkinter window so that I can add other labels next to the show.
this is my code i want to call the function in a tkinter window so that i can assign it to a button.
import cv2
import numpy as np
import glob
import os
import random
class Image:
def __init__(self, filename, time=200, size=800):
self.size = size
self.time = time
self.shifted = 1.0
self.img = cv2.imread(filename)
self.height, self.width, _ = self.img.shape
if self.width < self.height:
self.height = int(self.height*size/self.width)
self.width = size
self.img = cv2.resize(self.img, (self.width, self.height))
self.shift = self.height - size
self.shift_height = True
else:
self.width = int(self.width*size/self.height)
self.height = size
self.shift = self.width - size
self.img = cv2.resize(self.img, (self.width, self.height))
self.shift_height = False
self.delta_shift = self.shift/self.time
def reset(self):
if random.randint(0, 1) == 0:
self.shifted = 0.0
self.delta_shift = abs(self.delta_shift)
else:
self.shifted = self.shift
self.delta_shift = -abs(self.delta_shift)
def get_frame(self):
if self.shift_height:
roi = self.img[int(self.shifted):int(self.shifted) + self.size, :, :]
else:
roi = self.img[:, int(self.shifted):int(self.shifted) + self.size, :]
self.shifted += self.delta_shift
if self.shifted > self.shift:
self.shifted = self.shift
if self.shifted < 0:
self.shifted = 0
return roi
def process():
text = f'xXxxxxxXXXXXxxxxxXx'
coordinates = (650, 1100)
font = cv2.FONT_HERSHEY_SIMPLEX
fontScale = 2
color = (255, 0, 255)
thickness = 3
filenames = glob.glob(os.path.join(path, "*"))
cnt = 0
images = []
for filename in filenames:
img = Image(filename)
images.append(img)
if cnt > len(images):
break
cnt += 1
prev_image = images[random.randrange(0, len(images))]
prev_image.reset()
while True:
while True:
img = images[random.randrange(0, len(images))]
if img != prev_image:
break
img.reset()
for i in range(100):
alpha = i/100
beta = 1.0 - alpha
dst = cv2.addWeighted(img.get_frame(), alpha, prev_image.get_frame(), beta, 0.0)
dst = cv2.putText(dst, text, coordinates, font, fontScale, color, thickness,cv2.LINE_AA)
cv2.imshow('Slideshow', dst)
if cv2.waitKey(10) == ord('q'):
cv2.destroyWindow('Slideshow')
return
prev_image = img
for _ in range(100):
cv2.imshow('Slideshow', img.get_frame())
if cv2.waitKey(10) == ord('q'):
cv2.destroyWindow('Slideshow')
return
def start():
cnt = 0
images = []
path = 'pictures'
filenames = glob.glob(os.path.join(path, "*"))
showWindow = tk.Tk()
showWindow.attributes('-fullscreen', True)
showWindow.mainloop()
I tried to display the text in opencv in the pictures but I had problems keeping the text in the exercises. That's why I want to display the whole thing in a Tkinter window so I can do the classification with grid. because I don't just want to display a single image but a slide show
Does the method not work here(**) or I just don't understand it could someone help me.
(**)=
#Import the tkinter library
from tkinter import *
import numpy as np
import cv2
from PIL import Image, ImageTk
#Create an instance of tkinter frame
show_Winow = Tk()
win.geometry("700x550")
#Load the image
img = cv2.imread('tutorialspoint.png')
#Rearrange colors
blue,green,red = cv2.split(img)
img = cv2.merge((red,green,blue))
im = Image.fromarray(img)
imgtk = ImageTk.PhotoImage(image=im)
#Create a Label to display the image
Label(show_Window, image= imgtk).pack()
show_Window.mainloop()

How To Draw a Triangle-Arrow With The Positions of Detected Objects

I am making a object detection project.
I have my code. And I have written it by following a tutorial. In the tutorial, the guy drew a rectangle in opencv for every single object which is detected.
But I want to change the rectangle to triangle or Arrow.
let me explain with code===>
In my function, I detect objects.
And here I draw rectangle for detected objects==>
cv2.rectangle(img, (x, y), (x+w,y+h), (255, 0 , 255), 2)
But I want to change this rectangle to a triangle.(And I want to set position of triangle to above of object.
Just like in these images:::
This is the object detection with triangle
[![enter image description here][1]][1]
This is the thing that what I want to make instead of rectangle:::
[![enter image description here][2]][2]
How Can I make a triangle/arrow with positions of my detected objects?
All of my code is here==>
from os.path import sep
import cv2 as cv2
import numpy as np
import json
# Camera feed
cap_cam = cv2.VideoCapture(0)
ret, frame_cam = cap_cam.read()
hey = 0
print(cv2. __version__)
whT = 320
confThreshold =0.5
nmsThreshold= 0.2
classesFile = "coco.names"
classNames = []
with open(classesFile, 'rt') as f:
classNames = f.read().rstrip('\n').split('\n')
print(classNames)
## Model Files
modelConfiguration = "custom-yolov4-tiny-detector.cfg"
modelWeights = "custom-yolov4-tiny-detector_last.weights"
net = cv2.dnn.readNetFromDarknet(modelConfiguration, modelWeights)
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)
def findObjects(outputs,img):
global hey
global previousHey
hT, wT, cT = img.shape
bbox = []
classIds = []
confs = []
for output in outputs:
for det in output:
scores = det[5:]
classId = np.argmax(scores)
confidence = scores[classId]
if confidence > confThreshold:
w,h = int(det[2]*wT) , int(det[3]*hT)
x,y = int((det[0]*wT)-w/2) , int((det[1]*hT)-h/2)
bbox.append([x,y,w,h])
classIds.append(classId)
confs.append(float(confidence))
global indicates
indices = cv2.dnn.NMSBoxes(bbox, confs, confThreshold, nmsThreshold)
hey = 0
for i in indices:
i = i[0]
box = bbox[i]
x, y, w, h = box[0], box[1], box[2], box[3]
# print(x,y,w,h)
cv2.rectangle(img, (x, y), (x+w,y+h), (255, 0 , 255), 2)
#cv2.line(img, (350,400), (x, y), (255,0,0), 4)
#cv2.line(img, (400,400), (x + 50 , y), (255,0,0), 4)
#cv.putText(img,f'{classNames[classIds[i]].upper()} {int(confs[i]*100)}%',
#(x, y-10), cv.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 2)
print('success')
hey = 1
video_frame_counter = 0
while cap_cam.isOpened():
img = cv2.imread('photos' + sep + 'lutfen.jpg')
#BURADA OK VİDEOSU OYNATILACAK
#if not decetiona diye dene yarın.
blob = cv2.dnn.blobFromImage(img, 1 / 255, (whT, whT), [0, 0, 0], 1, crop=False)
net.setInput(blob)
layersNames = net.getLayerNames()
outputNames = [(layersNames[i[0] - 1]) for i in net.getUnconnectedOutLayers()]
outputs = net.forward(outputNames)
findObjects(outputs,img)
cv2.imshow('Image', img)
# Video feed
if hey == 1:
filename = 'photos' + sep + 'Baslksz-3.mp4'
cap_vid = cv2.VideoCapture(filename)
if hey == 0:
filename = 'photos' + sep + 'vid2.mp4'
cap_vid = cv2.VideoCapture(filename)
print(hey)
ret, frame_vid = cap_vid.read()
#cap_cam.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
#cap_cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
# Resize the camera frame to the size of the video
height = int(cap_vid.get(cv2.CAP_PROP_FRAME_HEIGHT))
width = int(cap_vid.get(cv2.CAP_PROP_FRAME_WIDTH))
# Capture the next frame from camera
ret, frame_cam = cap_cam.read()
video_frame_counter += 1
if video_frame_counter == cap_vid.get(cv2.CAP_PROP_FRAME_COUNT):
video_frame_counter = 0
cap_vid.set(cv2.CAP_PROP_POS_FRAMES, 0)
frame_cam = cv2.resize(frame_cam, (width, height), interpolation = cv2.INTER_AREA)
#ret = cap_vid.set(cv2.CAP_PROP_POS_MSEC, time_passed)
ret, frame_vid = cap_vid.read()
if not ret:
print('Cannot read from video stream')
break
# Blend the two images and show the result
tr = 0.4 # transparency between 0-1, show camera if 0
frame = ((1-tr) * frame_cam.astype(np.float) + tr * frame_vid.astype(np.float)).astype(np.uint8)
cv2.imshow('Transparent result', frame)
if cv2.waitKey(1) == 27: # ESC is pressed
break
cap_cam.release()
cap_vid.release()
cv2.destroyAllWindows()
The easy way
You can use the cv.arrowedLine() function that will draw something similar to what you want. For example, to draw a red arrow above your rectangle:
center_x = x + w//2
cv2.arrowedLine(img, (center_x, y-50), (center_x, y-5), (0,0,255), 2, 8, 0, 0.5)
which should give a result similar to the image below. Take a look at the OpenCV documentation for the description of the parameters of the function. You can change its size, thickness, color, etc.
Custom arrow shape
If you want more control over the shape of your arrow, you can define a contour (vertex by vertex) and use cv.drawContours() to render it. For example:
# define the arrow shape
shape = np.array([[[0,0],[-25,-25],[-10,-25],[-10,-50],
[10,-50],[10,-25],[25,-25]]])
# move it to the desired position
cx = x + w // 2
cy = y - 5
shape[:,:,0] += cx
shape[:,:,1] += cy
# draw it
cv2.drawContours(img, shape, -1, (0, 255, 0), -1)
This snippet will give you the image below. You can adjust the shape by altering the vertices in the shape array, or look at the documentation to change the way OpenCV draws it.

How to draw slick lines using PIL and Tkinter

I'm making a Pixel Art package with Pillow (PIL) and Tkinter. So far, I have set up a code that draws a continuous line on mouse motion path. However, the line isn't slick.
I'm tried drawing five pixels x-2, x-1, x, x+1, x+2 yet still, the line isnt't slick.
import tkinter as tk
from PIL import Image
def tinker(img, pos, color):
pixels = img.load()
x = pos[0]
y = pos[1]
if (x > 3 and y > 3) and (x < 253 and y < 253):
pixels[x-2, y] = color
pixels[x - 1, y] = color
pixels[x , y] = color
pixels[x + 1, y] = color
pixels[x + 2, y] = color
elif x < 3 and y < 3:
x = 3
y = 3
elif x > 253 and y > 253:
x = 253
y = 253
img.save("image.png")
root = tk.Tk()
img = Image.new("RGB", (256, 256), color="white")
img.save("image.png")
openimg = tk.PhotoImage(file="image.png")
panel = tk.Label(root, image=openimg)
panel.pack(side="bottom", fill="both", expand="yes")
def callback(e):
x, y = e.x, e.y
print(f"({x}, {y})")
tinker(img, pos=(x, y), color=(0, 0, 0))
img2 = tk.PhotoImage(file="image.png")
panel.configure(image=img2)
panel.image = img2
root.bind("<B1-Motion>", callback)
root.mainloop()
I expected slick lines, a la Photoshop, GIMP and Paint.net.

PIL Image in canvas not visible

I tried to create an Image using PIL. I wanted to show the image in a tkinter canvas with "create_image" method. But there is no image in the canvas. It looks empty:
self.image = Image.new("RGBA", (w, h), "white")
pixels = self.image.load()
for x in range(w):
for y in range(h):
pixels[x, y] = area.get_color(x, y)
photo = ImageTk.PhotoImage(self.image)
self.canvas.create_image(0, 0, image=photo)
self.canvas.pack(fill=tk.BOTH, expand=tk.TRUE)
The method "area.get_color(x, y)" returns a 4-tuple (r, g, b, alpha).
Well, few things:
First of all, python has this weird garbage collecting issue with tkinter images, in order to really pass the image to your canvas, you need to anchor down first otherwise it would just got wiped when it was passed to canvas.
This is what i did after learning from other people's example:
window.image = create_image(WIDTH, HEIGHT)
Once you anchor it down to your Tk(), it shouldn't get collected and erased anymore as long as your Tk() exists.
Second problem is your placement of image, you might want to place it to the center of your canvas instead of the corner of it:
canvas.create_image(WIDTH//2, HEIGHT//2, image=window.image)
In the end your program would look like this:
window = tk.Tk()
canvas = tk.Canvas(window, width=WIDTH, height=HEIGHT)
window.image = create_image(WIDTH, HEIGHT)
canvas.create_image(WIDTH//2, HEIGHT//2, image=window.image)
canvas.pack(fill=tk.BOTH, expand=tk.TRUE)
window.mainloop()
BTW a circle of a radius of 10 is just too small in a canvas of 640 x 480, you might want to increase this number to 100 or so.
if x**2 + y**2 < 10000:
Like that^
I posted just the relevant part of a pupils project code. They tried to create a tkinter application showing a fractal. But they should do it in
an absolute (crazy) objectoriented manner. After that it should be done in a very much faster implementation.
All the other code works fine (all tests ok), only the PIL part ...
Ok i create a small piece of code to show the pupils idea. But this code doesn't show a black piece of a circle as expected. The canvas is still empty:
import tkinter as tk
from PIL import Image, ImageTk
WHITE = (255, 255, 255, 255)
BLACK = (0, 0, 0, 255)
WIDTH = 640
HEIGHT = 480
def get_color(x, y):
if x**2 + y**2 < 100:
return BLACK
else:
return WHITE
def create_image(w, h):
image = Image.new("RGBA", (w, h), "white")
pixels = image.load()
for x in range(w):
for y in range(h):
pixels[x, y] = get_color(x, y)
return ImageTk.PhotoImage(image)
window = tk.Tk()
canvas = tk.Canvas(window, width=WIDTH, height=HEIGHT)
canvas.create_image(0, 0, image=create_image(WIDTH, HEIGHT))
canvas.pack(fill=tk.BOTH, expand=tk.TRUE)
window.mainloop()

Additive color with Tkinter

I'm trying to reproduce additive color with Tkinter.
My function :
def synthese(red,green,blue):
win2 = Tk()
win2.title("ADDITIVE COLOR")
win2.geometry("500x500")
win2.resizable(0,0)
hred = "#%02x%02x%02x" % (red, 0, 0) #RGB to Hexadecimal
hgreen = "#%02x%02x%02x" % (0, green, 0)
hblue = "#%02x%02x%02x" % (0, 0, blue)
r = 50
Width = 450
Height = 450
win3 = Canvas(win2, width = Width, height = Height, bg = 'white')
win3.pack(padx=5,pady=5)
win3.create_oval(10,150,300,440, outline=hred, fill=hred)
win3.create_oval(150,150,440,440, outline=hblue, fill=hblue)
win3.create_oval(75,10,375,300, outline=hgreen, fill=hgreen)
win2.mainloop()
What I get :
And what I would like :
It is possible to merge the colors or I need to find the collision zones?
You can use ImageChops to add images.
So you can do something like this:
from Tkinter import Tk, Canvas, Label
import ImageDraw, ImageChops, Image, ImageTk
image1 = Image.new("RGBA", (500, 500), color=0)
image2 = Image.new("RGBA", (500, 500), color=0)
image3 = Image.new("RGBA", (500, 500), color=0)
draw1 = ImageDraw.Draw(image1)
draw2 = ImageDraw.Draw(image2)
draw3 = ImageDraw.Draw(image3)
draw1.ellipse([10, 150, 300, 440], (128,0,0))
draw2.ellipse([150, 150, 440, 440], (0,0,128))
draw3.ellipse([75, 10, 375, 300], (0,128,0))
out = ImageChops.add(image1,image2,0.5)
out = ImageChops.add(out,image3,0.5)
win2 = Tk()
photo = ImageTk.PhotoImage(out)
label = Label(win2, image=photo)
label.pack()
win2.mainloop()
output:
Here's a way to draw additive RGB circles using Numpy. It converts the Numpy data to a Tkinter PhotoImage object using PIL (Pillow), and displays the results in a Tkinter Label. I use a black background because we're doing additive color mixing.
import numpy as np
from PIL import Image, ImageTk
import tkinter as tk
width, height = 400, 360
# Make RGB colors
red, grn, blu = np.eye(3, dtype=np.uint8) * 255
class GUI:
def __init__(self, width, height):
self.root = root = tk.Tk()
root.title('Circles')
root.geometry('%dx%d' % (width, height))
self.img_label = tk.Label(self.root)
self.img_label.pack(fill='both', expand=True)
gui = GUI(width, height)
# Increase the scale for smoother circles
scale = 4
width *= scale
height *= scale
screen = np.zeros((height, width, 3), dtype=np.uint8)
def show(fname=None):
img = Image.fromarray(screen, 'RGB')
img = img.resize((width // scale, height // scale), resample=Image.BILINEAR)
gui.photo = ImageTk.PhotoImage(image=img)
gui.img_label.config(image=gui.photo)
gui.root.update()
if fname is not None:
img.save(fname)
def disc(radius):
diameter = 2 * radius
yy, xx = np.mgrid[:diameter, :diameter] - radius
c = xx * xx + yy * yy < radius * radius
return c.reshape(diameter, diameter, 1)
def get_region(cx, cy, radius):
ylo = cy - radius
yhi = cy + radius
xlo = cx - radius
xhi = cx + radius
return screen[ylo:yhi, xlo:xhi]
radius = 120 * scale
circle = disc(radius)
cx = width // 2
cy = 130 * scale
region = get_region(cx, cy, radius)
region |= circle * red
show()
cy += 97 * scale
cx -= 56 * scale
region = get_region(cx, cy, radius)
region |= circle * grn
show()
cx += 112 * scale
region = get_region(cx, cy, radius)
region |= circle * blu
show('rgb.png')
gui.root.mainloop()
output
Using PIL you can create three grayscale layers, draw circles and use them to create expected circles but on black background.
If you use inverted layers then you get white background but with wrong circles.
With PIL you can even display it or save in file.
from PIL import Image, ImageDraw
def synthese(red=255, green=255, blue=255):
background = 0 # black
# layers in greyscale
layer_R = Image.new('L', (450, 450), background)
layer_G = Image.new('L', (450, 450), background)
layer_B = Image.new('L', (450, 450), background)
# draw circle on red layer
draw_R = ImageDraw.Draw(layer_R)
draw_R.ellipse((10,150,300,440), red)
# draw circle on green layer
draw_G = ImageDraw.Draw(layer_G)
draw_G.ellipse((150,150,440,440), green)
# draw circle on blue layer
draw_B = ImageDraw.Draw(layer_B)
draw_B.ellipse((75,10,375,300), blue)
#layer_R.show()
#layer_G.show()
#layer_B.show()
#layer_R.save('layer_r.png')
#layer_G.save('layer_g.png')
#layer_B.save('layer_b.png')
# create RGB image using greyscale layers
image_RGB = Image.merge('RGB', (layer_R, layer_G, layer_B))
# show it
image_RGB.show()
#image_RGB.save('rgb.png')
synthese(255, 255, 255)

Categories

Resources