python bokeh: get image from webcam and show it in dashboard - python

I want to display an image - e.g. capture with the webcam - in bokeh. I tried image_url and image_rgba, but both are not working. Image_url is showing nothing, image_rgb shows something, but there seems to be some index shift.
# -*- coding: utf-8 -*-
from bokeh.plotting import figure, show
import scipy.misc
import cv2
import matplotlib.pyplot as plt
import os
# capture video frame from webcam
#vc = cv2.VideoCapture(1)
vc = cv2.VideoCapture(-0)
rval, frame = vc.read()
vc.release()
# show captured image (figure 1)
fig = plt.figure()
ax = plt.Axes(fig,[0,0,1,1])
ax.set_axis_off()
fig.add_axes(ax)
ax.imshow(frame)
plt.show()
# save captured image
scipy.misc.imsave('outfile.jpg', frame)
mypath = os.path.join(os.getcwd(),'outfile.jpg')
# 1. try using image_url ... not working (figure 2)
p = figure()
p.image_url(url=[mypath],x=[0],y=[1],w=[10],h=[10])
show(p)
# 2. try using image_rgba ... also not working (figure 3)
p = figure(x_range=(0,10), y_range=(0,10))
p.image_rgba(image=[frame], x=0, y=0, dw=10, dh=10)
show(p)
# 3. solution provided by #bigreddot ... flipped but also not working (figure4)
img = scipy.misc.imread(mypath)[::-1] # flip y-direction for Bokeh
p = figure(x_range=(0,10), y_range=(0,10))
p.image_rgba(image=[img], x=0, y=0, dw=10, dh=10)
show(p)
update: according to #bigreddot 's comment
figure 1
figure 2
figure 3
figure 4
the bokeh version I use is 0.13 the python version 3.6.0

After investigation, the return result from OpenCV is a Numpy array of bytes with shape (M, N, 3), i.e. RGB tuples. What Bokeh expects is a Numpy array of shape (M, N) 32-bit integers representing RGBA values. So you need to convert from one format to the other. Here is a complete example:
from bokeh.plotting import figure, show
import scipy.misc
import cv2
import os
import numpy as np
#vc = cv2.VideoCapture(1)
vc = cv2.VideoCapture(-0)
rval, frame = vc.read()
vc.release()
M, N, _ = frame.shape
img = np.empty((M, N), dtype=np.uint32)
view = img.view(dtype=np.uint8).reshape((M, N, 4))
view[:,:,0] = frame[:,:,0] # copy red channel
view[:,:,1] = frame[:,:,1] # copy blue channel
view[:,:,2] = frame[:,:,2] # copy green channel
view[:,:,3] = 255
img = img[::-1] # flip for Bokeh
p = figure()
p.image_rgba(image=[img],x=0,y=0, dw=10, dh=10)
show(p)
Generates:

Related

Importing, editing, and displaying videos in spyder, python? (alternative to cv2.imshow?)

I am making a script in which you read a video file and detect and track object motion. I am loosely following this methodology: https://pyimagesearch.com/2015/05/25/basic-motion-detection-and-tracking-with-python-and-opencv/
However, I don't want to run the code from a command window, I want it to work within spyder. This is a problem as at the end of the code, it displays the finished video using the cv2.imshow(frame) command, which instantly crashes spyder for some reason. To get around this, I am trying to use matplotlib instead, but I just can't get the frames to replace each other in one window (ie put the frames back together to form a video again).
This is the code I am using:
def cv2_imshow(a, **kwargs):
a = a.clip(0, 255).astype('uint8')
# cv2 stores colors as BGR; convert to RGB
if a.ndim == 3:
if a.shape[2] == 4:
a = cv2.cvtColor(a, cv2.COLOR_BGRA2RGBA)
else:
a = cv2.cvtColor(a, cv2.COLOR_BGR2RGB)
return plt.imshow(a, **kwargs)
get_ipython().run_line_magic('matplotlib', 'qt')
plt.ion()
cv2_imshow(frame)
What I end up with is basically a new window being created for each frame all over my screen (ie it is not a video in one window)
Does anyone have a way of doing this?
Essentially I want the process to be this: read video --> detect motion, create frame with threshold and frame with moving object in red box --> repeat over all frames, creating 3 videos (or even just the finished video with the movement detection)
According to this post, it is not possible to update the "inline plots" in Spyder.
The closet solution I found is using clear_output as described in this post.
Code sample:
import cv2
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import clear_output
width, height, n_frames, fps = 320, 240, 10, 1
def cv2_imshow(a, **kwargs):
a = a.clip(0, 255).astype('uint8')
# cv2 stores colors as BGR; convert to RGB
if a.ndim == 3:
if a.shape[2] == 4:
a = cv2.cvtColor(a, cv2.COLOR_BGRA2RGBA)
else:
a = cv2.cvtColor(a, cv2.COLOR_BGR2RGB)
# https://matplotlib.org/stable/gallery/showcase/mandelbrot.html#sphx-glr-gallery-showcase-mandelbrot-py
dpi = 72
width, height = a.shape[1], a.shape[0]
fig = plt.figure(figsize=(width/dpi, height/dpi), dpi=dpi) # Create new figure
ax = fig.add_axes([0, 0, 1, 1], frameon=False, aspect=1) # Add axes to figure
ax.imshow(a, **kwargs)
plt.axis('off')
plt.show(block=False) # Show image without "blocking"
def make_image(i):
""" Build synthetic BGR image for testing """
p = width//60
im = np.full((height, width, 3), 60, np.uint8)
cv2.putText(im, str(i+1), (width//2-p*10*len(str(i+1)), height//2+p*10), cv2.FONT_HERSHEY_DUPLEX, p, (255, 30, 30), p*2) # Blue number
return im
# Show synthetic images in a loop
for i in range(n_frames):
a = make_image(i)
cv2_imshow(a)
plt.pause(1/fps)
# https://stackoverflow.com/a/59736741/4926757
clear_output(wait=False)

How to remove reflection from sunglasses and change the color of lenses in sunglasses

I am working on a project where I have to remove the reflection on sunglasses and also change the color of lenses. So, I tried to detect the dominant color in the image (sunglass lenses) and then tried to replace that color by another color using OpenCV.
But the code is not working properly. So, Please help me.
Here is the result that have obtained.
Here is the code that I have used to crop part of image(lenses) then detect the dominant color.
import cv2
from google.colab.patches import cv2_imshow
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import numpy as np
from collections import Counter
from skimage.color import rgb2lab, deltaE_cie76
import os
img = cv2.imread('originalimage.jpg')
cropped_lens2 = img[556:2045, 2000:3177]
image = cv2.cvtColor(cropped_lens2, cv2.COLOR_BGR2RGB)
modified_image = cv2.resize(image, (600, 400), interpolation = cv2.INTER_AREA)
cv2_imshow(modified_image)
modified_image = modified_image.reshape(modified_image.shape[0]*modified_image.shape[1], 3)
number_of_colors=2
clf = KMeans(n_clusters = number_of_colors)
labels = clf.fit_predict(modified_image)
counts = Counter(labels)
center_colors = clf.cluster_centers_
ordered_colors = [center_colors[i] for i in counts.keys()]
hex_colors = [RGB2HEX(ordered_colors[i]) for i in counts.keys()]
rgb_colors = [ordered_colors[i] for i in counts.keys()]
plt.figure(figsize = (8, 6))
plt.pie(counts.values(), labels = hex_colors, colors = hex_colors)
Input Image and output images are as shown below.
Original image
Cropped image
color range output
Code that I have used to replace range of colors by a single color.
you can also replace it by gradient colors.
from PIL import Image
img = Image.open("old_Test/DSC-0296 fold.jpg")
img = img.convert("RGB")
datas = img.getdata()
new_image_data = []
for item in datas:
if item[0] in list(range(0, 80)):
new_image_data.append((255, 204, 100))
else:
new_image_data.append(item)
img.putdata(new_image_data)
img
output image after replacing colors
Change color of any image using OpenCV:
Based on your comment maybe this can help you
You can make a mask from the part that needs to be changed.
def changeColor(im, msk, hue=130):
h, s, v = cv2.split(cv2.cvtColor(im.copy(), cv2.COLOR_BGR2HSV))
h[np.where(msk == 0)] = hue
return cv2.cvtColor(cv2.merge([h, s, v]), cv2.COLOR_HSV2BGR)
Of these 6 images, the top left is the main image I drew with a graphic software. The image at the bottom left is a mask that tells the algorithm where the image should change. Apart from these 2 images, the other 4 are function tests.

Denoising a photo with Python

I have the following image which is a scanned copy of an old book. I want to remove the noise in the background (which is a bit reddish) that is coming due to the scanning of the old photo.
Update:
After applying opencv, following the parameter settings in opencv doc, I am getting the following output.
Please help fixing this.
The code that I am using:
import numpy as np
import cv2
from matplotlib import pyplot as plt
def display_image_in_actual_size(im_data):
dpi = 80
height, width, depth = im_data.shape
# What size does the figure need to be in inches to fit the image?
figsize = width / float(dpi), height / float(dpi)
# Create a figure of the right size with one axes that takes up the full figure
fig = plt.figure(figsize=figsize)
ax = fig.add_axes([0, 0, 1, 1])
# Hide spines, ticks, etc.
ax.axis('off')
# Display the image.
ax.imshow(im_data, cmap='gray')
plt.show()
img = cv2.imread('scan03.jpg')
dst = cv2.fastNlMeansDenoisingColored(img,None,10,10,7,21)
display_image_in_actual_size(img)
display_image_in_actual_size(dst)
The color of some pixels which has near threshold pixel values will be affected, but that depends on the task, here is one solution that you might adjust the threshold to a value that suits your task, also you might remove the median filter, or reduce the sigma value(5) if it affects the text badly, you might have some undesired noise, but the text will be readable.
import numpy as np
import matplotlib.pyplot as plt
import cv2
# Read Image
img = cv2.imread('input.jpg')
# BGR --> RGB
RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# BGR --> Gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Set thresholds
th_white = 210
th_black = 85
# copy original gray
mask_white = gray.copy()
mask_black = gray.copy()
# Thresholding
mask_white[mask_white<th_white] = 0
mask_black[mask_black<th_black] = 0
mask_white[mask_white>=th_white] = 255
mask_black[mask_black>=th_black] = 255
# Median Filtering (you can remove if the text is not readable)
median_white = cv2.medianBlur(mask_white,5)
median_black = cv2.medianBlur(mask_black,5)
# Mask 3 channels
mask_white_3 = np.stack([median_white, median_white, median_white], axis=2)
mask_black_3 = np.stack([median_black, median_black, median_black], axis=2)
# Masking the image(in RGB)
result1 = np.maximum(mask_white_3, RGB)
result2 = np.minimum(mask_black_3, result1)
# Visualize the results
plt.imshow(result2)
plt.axis('off')
plt.show()
opencv library has couple of denoisong functions.
You can find reading with examples here

Array to image with colour map

I have some data in a 2048x2048 array that I want to convert to an image.
import numpy as np
from PIL import Image
path = 'E:\\petra_2018_backup\\final\\raw\data\zn_2_run\\'
file = 'Zn_2_Pos1-01537.tif'
im = Image.open(path+file)
a = np.array(im)
img = Image.frombytes('CMYK', (2048, 2048), a) # pass in the bytestring
img.save('pic.pdf')
img.show()
This result is quite dark and also has a mixture of green and blue colours. I should mention that the attached picture is a screendump of the result as the result picture is to large to attach.
It would also be usefull if people can advice on a way to compress the resulting image.
I simply could not find a solution using pillow. So I started using scikit.
Code as follows:
import numpy as np
from skimage import io
import matplotlib.pyplot as plt
path = 'E:\\petra_2018_backup\\final\\raw\data\zn_2_run\\'
file = 'Zn_2_Pos1-01537.tif'
im =io.imread(path+file,as_gray=True)
b = np.array([im],dtype=np.uint16 )
b[b<150]=150 #Modification to array not included in original code
b[b>6000]=6000 #Modification to array not included in original code
c=b.squeeze()
fig = plt.figure()
ax = plt.subplot(111)
ax = io.imshow(c)
fig.savefig('result_figure.png',dpi=320)
It produces the following plot.

getting the image from VideoCapture object using read()

I am reading a video frame by frame using:
vc = cv2.VideoCapture('test.avi')
and later on I check whether the frame has been read using:
if vc.isOpened():
rval,frame = vc.read()
else:
rval = False
Now the problem is , if I try to convert this frame to a numpy array using the following code:
PILImage = Image.fromstring("L",cv.GetSize(frame),frame.tostring())
NumPyArray = np.array(PILImage)
I get an error saying:
CvArr argument 'arr' must be IplImage, CvMat or CvMatND. Use fromarray() to convert numpy arrays to CvMat or cvMatND
From Documentation of VideoCapture::read I found that it returns two things [retVal,Image] . How do I get the Image part only and how do I convert it to Numpy Array?
Further Info:
Basically the whole point in doing this is that ,I am trying to write a program which allows me to go through each frame of the video by pressing spacebar, and select a particular region from any frame and save it as a jpg. Here is the Code:
from ITMS import ITMS
import cv2
from cv2 import cv
import numpy as np
import matplotlib.pyplot as plt
import Image
import matplotlib.widgets as widgets
def onselect(eclick, erelease):
if eclick.ydata>erelease.ydata:
eclick.ydata,erelease.ydata=erelease.ydata,eclick.ydata
if eclick.xdata>erelease.xdata:
eclick.xdata,erelease.xdata=erelease.xdata,eclick.xdata
ax.set_ylim(erelease.ydata,eclick.ydata)
ax.set_xlim(eclick.xdata,erelease.xdata)
fig.canvas.draw()
def subImager(arr):
fig = plt.figure()
ax = fig.add_subplot(111)
plt_image=plt.imshow(arr,cmap="Greys_r")
rs=widgets.RectangleSelector(
ax, onselect, drawtype='box',
rectprops = dict(facecolor='red', edgecolor = 'red', alpha=0.2, fill=True))
plt.show()
cv2.namedWindow("preview")
vc = cv2.VideoCapture('test.avi')
if vc.isOpened():
rval,frame = vc.read()
else:
rval = False
while rval:
key = cv2.waitKey(30)
if key==32:
cv2.imshow("preview", frame)
NumPyArray=ITMS.CVtoNPArray(frame)
subImager(NumPyArray)
rval,frame = vc.read()
elif key==27:
break
cv2.destroyAllWindows()
ITMS Class:
from cv2 import cv
import cv2
import numpy as np
from PIL import Image
class ITMS:
def __init__(self):
pass
def CVtoNPArray(CVImage):
PILImage = Image.fromstring("L",cv.GetSize(CVImage),CVImage.tostring())
NumPyArray = np.array(PILImage)
return NumPyArray
CVtoNPArray=staticmethod(CVtoNPArray)
You are over-complicating things, the image returned in your frame name is already a numpy array. If you want to convert it to PIL, simply do Image.fromarray(frame).

Categories