I'm building a GUI and I want to allow the user to change the contrast of an image displayed through a PyQtGraph GraphicsWindow (say using a scroll bar or something). Any ideas?
My code looks something like this:
gw = pg.GraphicsWindow(size=(OCT_WIDTH, OCT_HEIGHT), border=True)
gw_layout = gw.addLayout(row=0, col=0)
gw_view = gw_layout.addViewBox(row=1, col=0, lockAspect=True)
img = imread(imgPath)
imgItem = pg.ImageItem(img)
gw_view.addItem(imgItem)
The ImageView widget contains a color bar that can be used to modify the image contrast or intensity. See the ImageView example.
For convenience and future reference I copied the ImageView.py example here below.
# -*- coding: utf-8 -*-
"""
This example demonstrates the use of ImageView, which is a high-level widget for
displaying and analyzing 2D and 3D data. ImageView provides:
1. A zoomable region (ViewBox) for displaying the image
2. A combination histogram and gradient editor (HistogramLUTItem) for
controlling the visual appearance of the image
3. A timeline for selecting the currently displayed frame (for 3D data only).
4. Tools for very basic analysis of image data (see ROI and Norm buttons)
"""
## Add path to library (just for examples; you do not need this)
import initExample
import numpy as np
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
# Interpret image data as row-major instead of col-major
pg.setConfigOptions(imageAxisOrder='row-major')
app = QtGui.QApplication([])
## Create window with ImageView widget
win = QtGui.QMainWindow()
win.resize(800,800)
imv = pg.ImageView()
win.setCentralWidget(imv)
win.show()
win.setWindowTitle('pyqtgraph example: ImageView')
## Create random 3D data set with noisy signals
img = pg.gaussianFilter(np.random.normal(size=(200, 200)), (5, 5)) * 20 + 100
img = img[np.newaxis,:,:]
decay = np.exp(-np.linspace(0,0.3,100))[:,np.newaxis,np.newaxis]
data = np.random.normal(size=(100, 200, 200))
data += img * decay
data += 2
## Add time-varying signal
sig = np.zeros(data.shape[0])
sig[30:] += np.exp(-np.linspace(1,10, 70))
sig[40:] += np.exp(-np.linspace(1,10, 60))
sig[70:] += np.exp(-np.linspace(1,10, 30))
sig = sig[:,np.newaxis,np.newaxis] * 3
data[:,50:60,30:40] += sig
## Display the data and assign each frame a time value from 1.0 to 3.0
imv.setImage(data, xvals=np.linspace(1., 3., data.shape[0]))
## Set a custom color map
colors = [
(0, 0, 0),
(45, 5, 61),
(84, 42, 55),
(150, 87, 60),
(208, 171, 141),
(255, 255, 255)
]
cmap = pg.ColorMap(pos=np.linspace(0.0, 1.0, 6), color=colors)
imv.setColorMap(cmap)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
Related
I have an RGB image, which I immediately take the red component. I then convert the resulting grayscale into bytes and display it in Graph using draw_image. However, only the background is shown and the red component image is not displayed. Let img be my RGB image. Here is my code:
import cv2
import PySimpleGUI as sg
from PIL import Image, ImageTk
r,g,b = cv2.split(img)
data = bytes(Image.fromarray(r).tobytes())
width = len(b)
length = len(b[0])
layout = [[sg.Graph(
canvas_size=(length, width),
graph_bottom_left=(0, 0),
graph_top_right=(length, width),
key="-GRAPH-",
change_submits=True,
background_color='black',
drag_submits=True) ]]
window = sg.Window(layout, finalize=True)
window.Maximize()
graph = window["-GRAPH-"]
graph.draw_image(data = data, location=(0,width))
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
The result is nothing but black background. I have checked that the image img and the red component r are both correct (i.e. statements like imshow will give the right image). The problem therefore lies in either the line data = bytes(Image.fromarray(r).tobytes()) or graph.draw_image(data = data, location=(0,width)). However, both seem correct to me. What am I missing? Is there any workarounds? As a side note, I am not allowed to save any images.
Image.tobytes(encoder_name='raw', *args)
This method returns the raw image data from the internal storage. For compressed image data (e.g. PNG, JPEG) use save(), with a BytesIO parameter for in-memory data.
import io
import cv2
import PySimpleGUI as sg
from PIL import Image, ImageTk
img = cv2.imread('D:/images.jpg')
r,g,b = cv2.split(img)
im = Image.fromarray(r)
width, height = im.size
buffer = io.BytesIO()
im.save(buffer, format='PNG')
data = buffer.getvalue()
layout = [[sg.Graph(
canvas_size=(width, height),
graph_bottom_left=(0, 0),
graph_top_right=(width, height),
key="-GRAPH-",
change_submits=True,
background_color='black',
drag_submits=True) ]]
window = sg.Window('Title', layout, finalize=True)
# window.Maximize()
graph = window["-GRAPH-"]
graph.draw_image(data = data, location=(0, height))
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
window.close()
I'm trying to build a simple image classifier using scikit-learn. I'm hoping to avoid having to resize and convert each image before training.
Question
Given two different images that are different formats and sizes (1.jpg and 2.png), how can I avoid a ValueError while fitting the model?
I have one example where I train using only 1.jpg, which fits successfully.
I have another example where I train using both 1.jpg and 2.png and a ValueError is produced.
This example will fit successfully:
import numpy as np
from sklearn import svm
import matplotlib.image as mpimg
target = [1, 2]
images = np.array([
# target 1
[mpimg.imread('./1.jpg'), mpimg.imread('./1.jpg')],
# target 2
[mpimg.imread('./1.jpg'), mpimg.imread('./1.jpg')],
])
n_samples = len(images)
data = images.reshape((n_samples, -1))
model = svm.SVC()
model.fit(data, target)
This example will raise a Value error.
Observe the different 2.png image in target 2.
import numpy as np
from sklearn import svm
import matplotlib.image as mpimg
target = [1, 2]
images = np.array([
# target 1
[mpimg.imread('./1.jpg'), mpimg.imread('./1.jpg')],
# target 2
[mpimg.imread('./2.png'), mpimg.imread('./1.jpg')],
])
n_samples = len(images)
data = images.reshape((n_samples, -1))
model = svm.SVC()
model.fit(data, target)
# ValueError: setting an array element with a sequence.
1.jpg
2.png
For this, I would really recommend using the tools in Keras that are specifically designed to preprocess images in a highly scalable and efficient way.
from keras.preprocessing.image import ImageDataGenerator
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
1 Determine the target size of your new pictures
h,w = 150,150 # desired height and width
batch_size = 32
N_images = 100 #total number of images
Keras works in batches, so batch_size just determines how many pictures at once will be processed (this does not impact your end result, just the speed).
2 Create your Image Generator
train_datagen = ImageDataGenerator(
rescale=1./255)
train_generator = train_datagen.flow_from_directory(
'Pictures_dir',
target_size=(h, w),
batch_size=batch_size,
class_mode = 'binary')
The object that is going to do the image extraction is ImageDataGenerator. It has the method flow_from_directory which I believe might be useful for you here. It will read the content of the folder Pictures_dir and expect your images to be in folders by class (eg: Pictures_dir/class0 and Pictures_dir/class1). The generator, when called, will then create images from these folders and also import their label (in this example, 'class0' and 'class1').
There are plenty of other arguments to this generator, you can check them out in the Keras documentation (especially if you want to do data augmentation).
Note: this will take any image, be it PNG or JPG, as you requested
If you want to get the mapping from class names to label indices, do:
train_generator.class_indices
# {'class0': 0, 'class1': 1}
You can check what is going on with
plt.imshow(train_generator[0][0][0])
3 Extract all resized images from the Generator
Now you are ready to extract the images from the ImageGenerator:
def extract_images(generator, sample_count):
images = np.zeros(shape=(sample_count, h, w, 3))
labels = np.zeros(shape=(sample_count))
i = 0
for images_batch, labels_batch in generator: # we are looping over batches
images[i*batch_size : (i+1)*batch_size] = images_batch
labels[i*batch_size : (i+1)*batch_size] = labels_batch
i += 1
if i*batch_size >= sample_count:
# we must break after every image has been seen once, because generators yield indifinitely in a loop
break
return images, labels
images, labels = extract_images(train_generator, N_images)
print(labels[0])
plt.imshow(images[0])
Now you have your images all at the same size in images, and their corresponding labels in labels, which you can then feed into any scikit-learn classifier of your choice.
Its difficult because of the math operations behind the scene, (the details are out of scope) if you manage do so, lets say you build your own algorithm, still you would not get the desired result.
i had this issue once with faces with different sizes. maybe this piece of code give you starting point.
from PIL import Image
import face_recognition
def face_detected(file_address = None , prefix = 'detect_'):
if file_address is None:
raise FileNotFoundError('File address required')
image = face_recognition.load_image_file(file_address)
face_location = face_recognition.face_locations(image)
if face_location:
face_location = face_location[0]
UP = int(face_location[0] - (face_location[2] - face_location[0]) / 2)
DOWN = int(face_location[2] + (face_location[2] - face_location[0]) / 2)
LEFT = int(face_location[3] - (face_location[3] - face_location[2]) / 2)
RIGHT = int(face_location[1] + (face_location[3] - face_location[2]) / 2)
if UP - DOWN is not LEFT - RIGHT:
height = UP - DOWN
width = LEFT - RIGHT
delta = width - height
LEFT -= int(delta / 2)
RIGHT += int(delta / 2)
pil_image = Image.fromarray(image[UP:DOWN, LEFT:RIGHT, :])
pil_image.thumbnail((50, 50), Image.ANTIALIAS)
pil_image.save(prefix + file_address)
return True
pil_image = Image.fromarray(image)
pil_image.thumbnail((200, 200), Image.ANTIALIAS)
pil_image.save(prefix + file_address)
return False
Note : i wrote this long time ago maybe not a good practice
Some time around 2011, I wrote a Pycairo script to generate a PDF that included several fills of custom vector patterns. Today I re-ran it (Python 3.5.2, Pycairo 1.10.0) and was surprised to see that all these patterns were rendered as low-resolution rasterized bitmaps. I reduced my script to this minimal example:
#!/usr/bin/python3
import cairo
def main():
surface = cairo.PDFSurface("test.pdf", 100, 100)
ctx = cairo.Context(surface)
pattern = make_pattern()
ctx.rectangle(10, 10, 80, 80)
ctx.set_source(pattern)
ctx.fill()
surface.finish()
def make_pattern():
pattern_surface = cairo.PDFSurface(None, 32, 8)
ctx = cairo.Context(pattern_surface)
ctx.set_line_width(.5)
ctx.set_source_rgb(0,0,0)
ctx.move_to(5, 6)
ctx.line_to(27, 2)
ctx.stroke()
pattern = cairo.SurfacePattern(pattern_surface)
pattern.set_extend(cairo.EXTEND_REPEAT)
return pattern
if __name__ == "__main__":
main()
The resulting PDF, heavily zoomed, renders the pattern like this:
Eyeballing the text of the PDF file confirms that this is a bitmap. Using an SVGSurface produces similar results. Is there a way to revert to the old behaviour whereby PDF pattern fills were rendered as vector fills in the final PDF rather than being rasterized like this? The only reference I've found online to the problem is this unanswered question on the cairo mailing list from January 2012.
I still haven't found a way to do this strictly using Pycairo, but I have found a solution using cairocffi, an improved, drop-in replacement for Pycairo. cairocffi offers the class RecordingSurface,
a surface that records all drawing operations at the highest level of the surface backend interface, (that is, the level of paint, mask, stroke, fill, and show_text_glyphs). The recording surface can then be “replayed” against any target surface by using it as a source surface.
I modified the script to use cairocffi and RecordingSurface:
#!/usr/bin/python3
import cairocffi as cairo
def main():
surface = cairo.PDFSurface("test.pdf", 100, 100)
ctx = cairo.Context(surface)
pattern = make_pattern()
ctx.rectangle(10, 10, 80, 80)
ctx.set_source(pattern)
ctx.fill()
surface.finish()
def make_pattern():
pattern_surface = \
cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, (0, 0, 32, 8))
ctx = cairo.Context(pattern_surface)
ctx.set_line_width(.5)
ctx.set_source_rgb(0,0,0)
ctx.move_to(5, 6)
ctx.line_to(27, 2)
ctx.stroke()
pattern = cairo.SurfacePattern(pattern_surface)
pattern.set_extend(cairo.EXTEND_REPEAT)
return pattern
if __name__ == "__main__":
main()
This resulted in a non-rasterized pattern:
I am using Tkinter and the grid() layout manager to create a GUI. I am showing the image in my GUI using a label, on a tabbed window:
label2 = ttk.Label(tab2)
image2 = PhotoImage(file="lizard.gif")
label2['image'] = image2
label2.grid(column=0, row=0, columnspan=3)
For illustration, let's say the image is 300 x 900. If I know a set of coordinates within the image, how can I overlay a shaded box on the image, defined by the known (A,B,C,D which are shown just for the illustration purpose) coordinates?
Let me give you a step by step solution.
You can use a tkinter.Label() to display your image as you did, you can also choose other widgets. But for situation, let's choose tkinter.Canvas() widget instead (but same reasoning is valid if you choose to use tkinter.Label())
Technical issues:
Your problem contains 2 main sub-problems to resolve:
How to overlay 2 images the way you want.
How to display an image using tkinter.Canvas()
To be able to read an image of jpg format , you need to use a specific PIL (or its Pillow fork) method and a class:
PIL.Image.open()
PIL.ImageTk.PhotoImage()
This is done by 3 lines in the below program:
self.im = Image.open(self.saved_image)
self.photo = ImageTk.PhotoImage(self.im)
And then display self.photo in the self.canvas widget we opted for:
self.canvas.create_image(0,0, anchor=tk.N+tk.W, image = self.photo)
Second, to reproduce the effect you desire, use cv2.addWeighted() OpenCV method. But I feel you have already done that. So I just show you the portion of code of the program that does it:
self.img = cv2.imread(self.image_to_read)
self.overlay = self.img.copy()
cv2.rectangle(self.overlay, (500,50), (400,100), (0, 255, 0), -1)
self.opacity = 0.4
cv2.addWeighted(self.overlay, self.opacity, self.img, 1 - self.opacity, 0, self.img)
cv2.imwrite( self.saved_image, self.img)
Program design:
I use 2 methods:
- __init__(): Prepare the frame and call the GUI initialization method.
- initialize_user_interface(): Draw the GUI and perform the previous operations.
But for scalability reasons, it is better to create a separate method to handle the different operations of the image.
Full program (OpenCV + tkinter)
Here is the source code (I used Python 3.4):
'''
Created on Apr 05, 2016
#author: Bill Begueradj
'''
import tkinter as tk
from PIL import Image, ImageTk
import cv2
import numpy as np
import PIL
class Begueradj(tk.Frame):
'''
classdocs
'''
def __init__(self, parent):
'''
Prepare the frame and call the GUI initialization method.
'''
tk.Frame.__init__(self, parent)
self.parent=parent
self.initialize_user_interface()
def initialize_user_interface(self):
"""Draw a user interface allowing the user to type
"""
self.parent.title("Bill BEGUERADJ: Image overlay with OpenCV + Tkinter")
self.parent.grid_rowconfigure(0,weight=1)
self.parent.grid_columnconfigure(0,weight=1)
self.image_to_read = 'begueradj.jpg'
self.saved_image = 'bill_begueradj.jpg'
self.img = cv2.imread(self.image_to_read)
self.overlay = self.img.copy()
cv2.rectangle(self.overlay, (500,50), (400,100), (0, 255, 0), -1)
self.opacity = 0.4
cv2.addWeighted(self.overlay, self.opacity, self.img, 1 - self.opacity, 0, self.img)
cv2.imwrite( self.saved_image, self.img)
self.im = Image.open(self.saved_image)
self.photo = ImageTk.PhotoImage(self.im)
self.canvas = tk.Canvas(self.parent, width = 580, height = 360)
self.canvas.grid(row = 0, column = 0)
self.canvas.create_image(0,0, anchor=tk.N+tk.W, image = self.photo)
def main():
root=tk.Tk()
d=Begueradj(root)
root.mainloop()
if __name__=="__main__":
main()
Demo:
This is a screenshot of the running program:
You will need to use a canvas widget. That will allow you to draw an image, and then overlay a rectangle on it.
Although the above answers were wonderfully in depth, they did not fit my exact situation (Specifically use of Python 2.7, etc.). However, this solution gave me exactly what I was looking for:
canvas = Canvas(tab2, width=875, height=400)
image2=PhotoImage(file='lizard.gif')
canvas.create_image(440,180,image=image2)
canvas.grid(column=0, row=0, columnspan=3)
The rectangle is added over the canvas using:
x1 = 3, y1 = 10, x2 = 30, y2 = 20
canvas.create_rectangle(x1, y1, x2, y2, fill="blue", stipple="gray12")
stipple comes from this example, to help add transparency to the rectangle.
This question already has answers here:
Animated sprite from few images
(4 answers)
How do I create animated sprites using Sprite Sheets in Pygame?
(1 answer)
Closed 2 years ago.
So I'm testing out this spritesheet code that I found on this site, and from another python file I wrote, I am trying to go and pass a spritesheet to it, where it will cut it up into a series of images, and allow to animate my monster in my game.
However, when I try to run it, I get this error:
python wormTest.py
Traceback (most recent call last):
File "wormTest.py", line 49, in <module>
worm = Worm()
File "wormTest.py", line 38, in __init__
img = pygame.image.load(outfile.getvalue())
TypeError: must be string without null bytes, not str
I was told to use CStringIO to help handle image input and output, but even after looking at the documentation, it is still a bit fuzzy to me. What is going on here?
The code I wrote for the worm:
import pygame
from sprite_sheet import sprite_sheet #handles sprite sheet stuff
import cStringIO #input output for images
# Define some colors
black = ( 0, 0, 0)
white = ( 255, 255, 255)
red = ( 255, 0, 0)
#initialize pygame
pygame.init()
#set the height and width of the screen
width = 800
height = 480
mainScreen = pygame.display.set_mode([width,height])
#A list of all of the sprites in the game
all_sprites_list = pygame.sprite.Group()
with open("big_worm.png", "rb") as infile:
outfile = cStringIO.StringIO()
outfile.write(infile.read())
class Worm(pygame.sprite.Sprite):
'''class that builds up the worm sprite'''
#constructor function
def __init__(self):
#call up the parent's constructor
pygame.sprite.Sprite.__init__(self)
images =[]
img = pygame.image.load(outfile.getvalue())
img.set_colorkey(white)#sets the color key. any pixel with same color as colorkey will be trasnparent
images = sprite_sheet(img, 40) #make up a sprite sheet
self.image = images[0]
self.rect = self.image.get_rect()
#creates a player object
worm = Worm()
#adds the player object to the all_sprites_list
all_sprites_list.add(worm)
#a conditional for the loop that keeps the game running until the user Xes out
done = False
#clock for the screen updates
clock = pygame.time.Clock()
#
# Game Logic Code
#
while done==False:
for event in pygame.event.get(): #user did something
if event.type == pygame.QUIT: #if the user hit the close button
done=True
mainScreen.fill(white)#makes the background white, and thus, the white part of the images will be invisible
#player.changeImage()
#limit the game to 20 fps
clock.tick(20)
#update the screen on the regular
pygame.display.flip()
pygame.quit()
And the code I'm using for the sprite_sheet:
#!/usr/bin/python
#
# Sprite Sheet Loader - hammythepig
#
# Edited by Peter Kennedy
#
# License - Attribution - hammythepig
#http://stackoverflow.com/questions/10560446/how-do-you-select-a-sprite-image-from-a-sprite-sheet-in-python
#
# Version = '2.0'
import pygame,sys
from pygame.locals import *
import cStringIO
def sprite_sheet(size,file,pos=(0,0)):
#Initial Values
len_sprt_x = size #sprite size
len_sprt_y = size
sprt_rect_x,sprt_rect_y = pos #where to find first sprite on sheet
sheet = pygame.image.load(file).convert_alpha() #Load the sheet
sheet_rect = sheet.get_rect()
sprites = []
print sheet_rect.height, sheet_rect.width
for i in range(0,sheet_rect.height-len_sprt_y,size[1]):#rows
print "row"
for i in range(0,sheet_rect.width-len_sprt_x,size[0]):#columns
print "column"
sheet.set_clip(pygame.Rect(sprt_rect_x, sprt_rect_y, len_sprt_x, len_sprt_y)) #find sprite you want
sprite = sheet.subsurface(sheet.get_clip()) #grab the sprite you want
sprites.append(sprite)
sprt_rect_x += len_sprt_x
sprt_rect_y += len_sprt_y
sprt_rect_x = 0
print sprites
return sprites
#VERSION HISTORY
#1.1 - turned code into useable function
#2.0 - fully functional sprite sheet loader