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()
Related
I am trying to extract RGB values from multiple cropped images using a single image. I want to save these RGB values into a csv file. For the same I have written the code mentioned below and the image is also attached below (color.jpg). But this code saves only the last cropped image RGB values. I want to save the RGB values for all cropped images. Could anyone suggest to me what changes I have to make for this code?
Thank you in advance
Python code:
from __future__ import with_statement
import cv2
import numpy as np
import csv
#image_path
img_path="gr.jpg"
#read image
img_raw = cv2.imread(img_path)
#select ROIs function
ROIs = cv2.selectROIs("Select Rois",img_raw)
#print rectangle points of selected roi
print(ROIs)
#Crop selected roi ffrom raw image
#roi_cropped = img_raw[int(roi[1]):int(roi[1]+roi[3]), int(roi[0]):int(roi[0]+roi[2])]
#counter to save image with different name
crop_number=0
#loop over every bounding box save in array "ROIs"
for rect in ROIs:
x1=rect[0]
y1=rect[1]
x2=rect[2]
y2=rect[3]
#crop roi from original image
img_crop=img_raw[y1:y1+y2,x1:x1+x2]
b,g,r = cv2.split(img_crop)
#Average RGB of the cropped image
B = b.mean()
G = g.mean()
R = r.mean()
#show cropped image
cv2.imshow("crop"+str(crop_number),img_crop)
#save cropped image
cv2.imwrite("crop"+str(crop_number)+".jpg",img_crop)
#Open a file to write the pixel data
with open('output_file.csv', 'w', newline='') as f_output:
csv_output = csv.writer(f_output)
csv_output.writerow(["img_name", "R", "G", "B"])
csv_output.writerow(["crop"+str(crop_number), R, G, B])
crop_number+=1
#hold window
cv2.waitKey(0)
cv2.destroyAllWindows()
[color.jpg][1]
[1]: https://i.stack.imgur.com/DpJD5.jpg
I think you just want to open for "append":
with open('output_file.csv', 'a', newline='') as f_output:
I'm placeing a rotated image on top of another image of different anchor point in the same figure. However the top image partially covers the bottom image, shown below. Is there a way to remove the black border of the rotated image?
Sample codes here:
from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
from bokeh.plotting import figure, ColumnDataSource, show
from bokeh.layouts import column
from bokeh.models.tools import PanTool, BoxZoomTool, WheelZoomTool, \
UndoTool, RedoTool, ResetTool, SaveTool, HoverTool
import numpy as np
from collections import namedtuple
from scipy import ndimage
def make_document(doc):
p = figure(match_aspect=True)
Anchor = namedtuple('Anchor', ['x', 'y'])
img1 = np.random.rand(256, 256)
anchor1 = Anchor(x=0, y=0)
img2= np.random.rand(256, 256)
anchor2 = Anchor(x=100, y=100)
img2 = ndimage.rotate(img2, 45, reshape=True)
p.image(image=[img1], x=anchor1.x, y=anchor1.y,
dw=img1.shape[0], dh=img1.shape[1], palette="Greys256")
p.image(image=[img2], x=anchor2.x, y=anchor2.y,
dw=img2.shape[0], dh=img2.shape[1], palette="Greys256")
doc.add_root(column(p, sizing_mode='stretch_both'))
apps = {'/': make_document}
server = Server(apps)
server.start()
server.io_loop.add_callback(server.show, "/")
try:
server.io_loop.start()
except KeyboardInterrupt:
print('keyboard interruption')
print('Done')
When you rotate an image, the new empty regions (black triangles on your image) are by default initialized with 0 (check out the mode and cval options at https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.rotate.html).
If you have a value that you know for sure will never be used in an image, you can pass it as cval. Then, you should be able to manually create a color mapper that maps that value to a transparent pixel and use the mapper instead of the palette (the arg name would be color_mapper).
If you don't have such a value, then you will have to use image_rgba and just make sure that whatever cval you decide to use will result in a transparent pixel.
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.
from scipy.misc import imread
from matplotlib import pyplot
import cv2
from cv2 import cv
from SRM import SRM ## Module for Statistical Regional Segmentation
im = imread("lena.png")
im2 = cv2.imread("lena.png")
print type(im), type(im2), im.shape, im2.shape
## Prints <type 'numpy.ndarray'> <type 'numpy.ndarray'> (120, 120, 3) (120, 120, 3)
srm = SRM(im, 256)
segmented = srm.run()
srm2 = SRM(im2, 256)
segmented2 = srm2.run()
pic = segmented/256
pic2 = segmented2/256
pyplot.imshow(pic)
pyplot.imsave("onePic.jpg", pic)
pic = pic.astype('uint8')
cv2.imwrite("onePic2.jpg", pic2)
pyplot.show()
onePic.jpg gives the correct segmented image but onePic2.jpg gives a complete black image.
Converting the datatype to uint8 using pic = pic.astype('uint8') did not help. I still gives a black image!
onePic.jpg using pyplot.imsave():
onePic2.jpg using cv2.imwrite():
Please help!
Before converting pic to uint8, you need to multiply it by 255 to get the correct range.
Although I agree with #sansuiso, in my case I found a possible edge case where my images were being shifted either one bit up in the scale or one bit down.
Since we're dealing with unsigned ints, a single shift means a possible underflow/overflow, and this can corrupt the whole image.
I found cv2's convertScaleAbs with an alpha value of 255.0 to yield better results.
def write_image(path, img):
# img = img*(2**16-1)
# img = img.astype(np.uint16)
# img = img.astype(np.uint8)
img = cv.convertScaleAbs(img, alpha=(255.0))
cv.imwrite(path, img)
This answer goes into more detail.
I encountered a similar situation with face detection, I wonder if there is a better way to execute this, here is my solution here as a reference.
from deepface import DeepFace
import cv2
import matplotlib.pyplot as plt
# import image and output
img_path = "image.jpg"
detected_face = DeepFace.detectFace(img_path, target_size = (128, 128))
plt.imshow(detected_face)
# image color scaling and saving
detected_face = cv2.cvtColor( detected_face,cv2.COLOR_BGR2RGB)
detected_face = cv2.convertScaleAbs(detected_face, alpha=(255.0))
cv2.imwrite("image_thumbnail.jpg", detected_face)
Is it possible to have black-and-white and color image on same window by using opencv libraray? How can I have both of these images on same window?
fraxel's answer has solved the problem with old cv interface. I would like to show it using cv2 interface, just to understand how this easy in new cv2 module. (May be it would be helpful for future visitors). Below is the code:
import cv2
import numpy as np
im = cv2.imread('kick.jpg')
img = cv2.imread('kick.jpg',0)
# Convert grayscale image to 3-channel image,so that they can be stacked together
imgc = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
both = np.hstack((im,imgc))
cv2.imshow('imgc',both)
cv2.waitKey(0)
cv2.destroyAllWindows()
And below is the output I got:
Yes it is, here is an example, expaination in the comments:
import cv
#open color and b/w images
im = cv.LoadImageM('1_tree_small.jpg')
im2 = cv.LoadImageM('1_tree_small.jpg',cv.CV_LOAD_IMAGE_GRAYSCALE)
#set up our output and b/w in rgb space arrays:
bw = cv.CreateImage((im.width,im.height), cv.IPL_DEPTH_8U, 3)
new = cv.CreateImage((im.width*2,im.height), cv.IPL_DEPTH_8U, 3)
#create a b/w image in rgb space
cv.Merge(im2, im2, im2, None, bw)
#set up and add the color image to the left half of our output image
cv.SetImageROI(new, (0,0,im.width,im.height))
cv.Add(new, im, new)
#set up and add the b/w image to the right half of output image
cv.SetImageROI(new, (im.width,0,im.width,im.height))
cv.Add(new, bw, new)
cv.ResetImageROI(new)
cv.ShowImage('double', new)
cv.SaveImage('double.jpg', new)
cv.WaitKey(0)
Its in python, but easy to convert to whatever..
Small improvement to the code with modern writing
concatenate
instead of
hstack
that is discontinued (stack can also be used)
import cv2
import numpy as np
im = cv2.imread('kick.jpg')
img = cv2.imread('kick.jpg',0)
# Convert grayscale image to 3-channel image,so that they can be stacked together
imgc = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
both = np.concatenate((im,imgc), axis=1) #1 : horz, 0 : Vert.
cv2.imshow('imgc',both)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
img = cv2.imread("image.jpg" , cv2.IMREAD_GRAYSCALE)
cv2.imshow("my image",img)
cv2.waitkey(0)
cv2.destroyAllWindow
#The image file should be in the application folder.
#The output file will be 'my image' name.
#The bottom line is to free up memory.