Video streaming getting distorted QImage - python

I am trying to make a multi camera video streaming using OpenCV and have used PyQt for GUI. The code is running fine in Full HD but the streams are getting distorted when I change the resolution of the display. Can anyone tell me why is this happening?
Following are the screenshots for your reference:
Screenshot in 1920*1080(Full HD)
Screenshot in 1600*900
Screenshot in 1366*768
The code is below:
from PyQt4 import QtCore, QtGui
from threading import Thread
from collections import deque
from datetime import datetime
import time
import sys
import cv2
import imutils
class CameraWidget(QtGui.QWidget):
def __init__(self, width, height, stream_link=0, aspect_ratio=False, parent=None, deque_size=1):
super(CameraWidget, self).__init__(parent)
# Initialize deque used to store frames read from the stream
self.deque = deque(maxlen=deque_size)
self.screen_width = width
self.screen_height = height
self.maintain_aspect_ratio = aspect_ratio
self.camera_stream_link = stream_link
# Flag to check if camera is valid/working
self.online = False
self.capture = None
self.video_frame = QtGui.QLabel()
self.load_network_stream()
# Start background frame grabbing
self.get_frame_thread = Thread(target=self.get_frame, args=())
self.get_frame_thread.daemon = True
self.get_frame_thread.start()
# Periodically set video frame to display
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.set_frame)
self.timer.start(.5)
print('Started camera: {}'.format(self.camera_stream_link))
def load_network_stream(self):
"""Verifies stream link and open new stream if valid"""
def load_network_stream_thread():
if self.verify_network_stream(self.camera_stream_link):
self.capture = cv2.VideoCapture(self.camera_stream_link)
self.online = True
self.load_stream_thread = Thread(target=load_network_stream_thread, args=())
self.load_stream_thread.daemon = True
self.load_stream_thread.start()
def verify_network_stream(self, link):
"""Attempts to receive a frame from given link"""
cap = cv2.VideoCapture(link)
if not cap.isOpened():
return False
cap.release()
return True
def get_frame(self):
"""Reads frame, resizes, and converts image to pixmap"""
while True:
try:
if self.capture.isOpened() and self.online:
# Read next frame from stream and insert into deque
status, frame = self.capture.read()
if status:
self.deque.append(frame)
else:
self.capture.release()
self.online = False
else:
# Attempt to reconnect
print('attempting to reconnect', self.camera_stream_link)
self.load_network_stream()
self.spin(2)
self.spin(.001)
except AttributeError:
pass
def spin(self, seconds):
"""Pause for set amount of seconds, replaces time.sleep so program doesnt stall"""
time_end = time.time() + seconds
while time.time() < time_end:
QtGui.QApplication.processEvents()
def set_frame(self):
"""Sets pixmap image to video frame"""
if not self.online:
self.spin(1)
return
if self.deque and self.online:
# Grab latest frame
frame = self.deque[-1]
# Keep frame aspect ratio
if self.maintain_aspect_ratio:
self.frame = imutils.resize(frame, width=self.screen_width)
# Force resize
else:
self.frame = cv2.resize(frame, (self.screen_width, self.screen_height))
# Convert to pixmap and set to video frame
self.img = QtGui.QImage(self.frame, self.frame.shape[1], self.frame.shape[0], QtGui.QImage.Format_RGB888).rgbSwapped()
self.pix = QtGui.QPixmap.fromImage(self.img)
self.video_frame.setPixmap(self.pix)
def get_video_frame(self):
return self.video_frame

I found the solution, QImage was responsible for the trouble.
else:
self.frame = cv2.resize(frame, (self.screen_width, self.screen_height))
self.frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
h, w, ch = self.frame.shape
bytesPerLine = ch * w
# Convert to pixmap and set to video frame
self.img = QtGui.QImage(self.frame, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
self.pix = QtGui.QPixmap.fromImage(self.img)
self.video_frame.setPixmap(self.pix)

Related

Displaying video frames through streamlit.empty() not working on Google Colab

I have a simple code that reads video stream from youtube for processing with OpenCV. I want to display these video frames in Streamlit app with st.empty(). On localhost it works fine, but when I run same code in Google Colab, only first frame is shown and then the video freezes. Is there some workaround?
Here is my code:
class ThreadedCamera(object):
def __init__(self, src=0):
self.capture = cv2.VideoCapture(src)
self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 2)
# FPS = 1/X
# X = desired FPS
self.FPS = 1/30
self.FPS_MS = int(self.FPS * 1000)
# Start frame retrieval thread
self.thread = Thread(target=self.update, args=())
self.thread.daemon = True
self.thread.start()
def update(self):
while True:
if self.capture.isOpened():
(self.status, self.frame) = self.capture.read()
time.sleep(self.FPS)
def show_frame(self):
cv2.imshow('frame', self.frame)
cv2.waitKey(self.FPS_MS)
if __name__ == '__main__':
src = 'YOUTUBE .m3u8 STREAM FILE'
threaded_camera = ThreadedCamera(src)
st.title("Real Time Face Emotion Detection Application")
st.header("Webcam Live Feed")
image_place = st.empty()
while True:
try:
image_place.image(threaded_camera.frame)
except AttributeError:
pass

Closing camera after a snapshot

I have this code below which creates a python gui to display a camera with a snapshot button. I would like to make it so that when a snapshot is taken the camera closes. I tried self.vid.release() and cv2.destroyAllWindows() inside the snapshot function. But it did not work. How can I close the camera after I take a snapshot?
import tkinter as tk
import cv2
import PIL.Image, PIL.ImageTk
import time
def camerachoice():
class App:
def __init__(self, window, window_title, video_source=0):
self.window = window
self.window.title(window_title)
self.video_source = video_source
self.vid = MyVideoCapture(self.video_source)
self.canvas = tk.Canvas(window, width = self.vid.width, height = self.vid.height)
self.canvas.pack()
self.btn_snapshot=tk.Button(window, text="Snapshot", width=50, command=self.snapshot)
self.btn_snapshot.pack(anchor=tk.CENTER, expand=True)
self.delay = 15
self.update()
self.window.mainloop()
def snapshot(self):
ret, frame = self.vid.get_frame()
if ret:
cv2.imwrite("frame-" + time.strftime("%d-%m-%Y-%H-%M- br%S") + ".jpg", cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
def update(self):
ret, frame = self.vid.get_frame()
if ret:
self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))
self.canvas.create_image(0, 0, image = self.photo, anchor = tk.NW)
self.window.after(self.delay, self.update)
class MyVideoCapture:
def __init__(self, video_source=0):
self.vid = cv2.VideoCapture(video_source)
if not self.vid.isOpened():
raise ValueError("Unable to open video source", video_source)
self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
def get_frame(self):
if self.vid.isOpened():
ret, frame = self.vid.read()
if ret:
return (ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
else:
return (ret, None)
else:
return (ret, None)
def __del__(self):
if self.vid.isOpened():
self.vid.release()
#App()
App(tk.Toplevel(), "Tkinter and OpenCV")
if __name__ == "__main__":
camerachoice()

How to read multi-camera streams with OpenCV seamlessly?

I went through this beautifully explained Q/A and I want something similar into a much stripped down version of the code I found there which includes PyQt4 and qdarkstyle.
My code is as follows:
import pafy
import base64
import zmq
from threading import Thread
import cv2
import time
class IPCamera(object):
def __init__(self, src=0):
self.frame = None
self.status = None
self.capture = cv2.VideoCapture(src)
self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 2)
self.FPS = 1/25
self.FPS_MS = int(self.FPS * 1000)
# Start frame retrieval thread
self.thread = Thread(target=self.update, args=())
self.thread.daemon = True
self.thread.start()
def update(self):
while True:
if self.capture.isOpened():
(self.status, self.frame) = self.capture.read()
# self.frame = cv2.resize(self.frame, (640, 480)) # resize the frame
time.sleep(self.FPS)
def show_frame(self):
cv2.imshow('frame', self.frame)
cv2.waitKey(self.FPS_MS)
if __name__ == '__main__':
# Sample youtube video starts
youtube_link = 'https://www.youtube.com/watch?v=QgaUKlAuqn8'
vPafy = pafy.new(youtube_link)
play = vPafy.getbest(preftype="mp4")
src = play.url
# Sample youtube video ends
# Creating ZMQ context starts
context = zmq.Context()
footage_socket = context.socket(zmq.PUB)
footage_socket.connect('tcp://localhost:5555')
# cv2.namedWindow("Client Started!")
# Creating ZMQ context ends
threaded_camera = IPCamera(src)
while threaded_camera.capture.isOpened():
try:
if threaded_camera.status is True:
encoded, buffer = cv2.imencode('.jpg', threaded_camera.frame)
jpg_as_text = base64.b64encode(buffer)
footage_socket.send(jpg_as_text)
# key = cv2.waitKey(1)
# threaded_camera.show_frame()
# if key == 27: # exit on ESC
# break
# else:
# break
except AttributeError:
pass
The above code is running fine, I want to implement the code from the Q/A on the aforementioned link with those libraries removed. Can you help me in that?

Memory Error & AttributeError in Python when move OpenCV's frame to Tkinter

I trying to move OpenCV's frame to Tkinter
Code Following...
import tkinter as tk
import time
from PIL import ImageTk, Image
from cv2 import cv2
To show the video
class CamWindow:
def __init__(self, window, title:str, cam_plug = 0, delay = 15):
self.delay = delay
self.window = window
self.title = title
self.window.title(self.title)
self.cam = MyVideoCapture(cam_plug)
self.canvas = tk.Canvas(self.window, width = self.cam.width, height = self.cam.height)
self.canvas.pack()
self.photoButton = tk.Button(self.window, text="SnapShot", width=30, command=self.shot)
self.photoButton.pack(anchor=tk.CENTER, expand=True)
self.update()
self.window.mainloop()
def shot(self):
ret, frame = self.cam.getPic()
if ret:
cv2.imwrite(time.strftime("%d-%m-%Y-%H-%M-%S")+".jpg", cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
def update(self):
ret, frame = self.cam.getPic()
if ret:
self.photo = ImageTk.PhotoImage(image=Image.fromarray(frame))
self.canvas.create_image(0, 0, image = self.photo, anchor = tk.NW)
self.window.after(self.delay, self.update())
class MyVideoCapture:
def __init__(self, cam_plug = 0):
self.cam = cv2.VideoCapture(cam_plug, cv2.CAP_DSHOW)
if not self.cam.isOpened():
raise ValueError("cannot open camera at %d", cam_plug)
self.width = self.cam.get(cv2.CAP_PROP_FRAME_WIDTH)
self.height = self.cam.get(cv2.CAP_PROP_FRAME_HEIGHT)
def getPic(self):
if self.cam.isOpened():
ret, frame = self.cam.read()
if ret:
return ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
else:
return ret, None
else:
return None, None
def __del__(self):
if self.cam.isOpened:
self.cam.release()
the terminal shows memory error, but everything seems to make sense.
and also I met "Attributeerror: 'photoimage' object has no attribute '_photoimage__photo'"
if there's any method or codes having problems please tell me as well
==========================edit===========================
my main file.py is:
from module.UI import CamWindow
def main():
CamWindow(tk.Tk(), title = "camera window", delay = 30)
if __name__ == '__main__':
main()

use threading in tkinter

I have two class and use tkinter demo.
class App:
def __init__(self, window, window_title, video_source=0):
self.window = window
self.window.title(window_title)
self.video_source = video_source
# open video source (by default this will try to open the computer webcam)
self.vid = MyVideoCapture(self.video_source)
# Create a canvas that can fit the above video source size
self.canvas = tkinter.Canvas(window).place(x=50, y=0)
# set plot parameter
self.fig = Figure(figsize=(7, 4), dpi=100)
self.fresh = FigureCanvasTkAgg(self.fig, master=self.window)
self.ax1 = self.fig.add_subplot(211)
self.ax2 = self.fig.add_subplot(212)
self.fresh.get_tk_widget().place(x=700, y=0)
self.window.geometry('1500x550')
# Camera thread
self.photo = None
self.delay = 15
self.t = threading.Thread(target=self.update, args=())
self.t.setDaemon(True)
self.t.start()
def refresh(self, data):
sample_track = pd.read_csv('/home/share/sample_track.csv')
x = [i for i in range(len(sample_track))]
y = sample_track['0']
xdata = [i for i in range(len(data))]
ydata = data
self.ax1.plot(x, y, 'bo--')
self.ax2.plot(xdata, ydata, 'ro--')
self.fresh.draw()
def update(self):
# Get a frame from the video source
ret, frame = self.vid.get_frame()
if ret:
self.photo = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(frame))
self.canvas.create_image(0, 0, image=self.photo, anchor=tkinter.NW)
self.window.after(self.delay, self.update)
class MyVideoCapture:
def __init__(self, video_source=0):
# Open the video source
self.vid = cv2.VideoCapture(video_source)
if not self.vid.isOpened():
raise ValueError("Unable to open video source", video_source)
self.ret = None
def get_frame(self):
if self.vid.isOpened():
self.ret, frame = self.vid.read()
if self.ret:
# Return a boolean success flag and the current frame converted to BGR
return self.ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
else:
return self.ret, None
if __name__ == '__main__':
win = tkinter.Tk()
panel = App(win, "Dance")
value = []
for i in range(15):
value.append(np.random.randint(0, 800))
panel.refresh(data=value)
time.sleep(0.1)
win.mainloop()
I want to start the Webcam and refresh the figure at the same time.
I tried to use the thread but still failed.
The following error occurred:
RuntimeError: main thread is not in main loop
AttributeError: 'PhotoImage' object has no attribute '_PhotoImage__photo'
How can I solve it?
Here is a working tkinter camera taken straight from here (found by a quick search for 'tkinter camera'):
import tkinter
import cv2
import PIL.Image, PIL.ImageTk
import time
class App:
def __init__(self, window, window_title, video_source=0):
self.window = window
self.window.title(window_title)
self.video_source = video_source
# open video source (by default this will try to open the computer webcam)
self.vid = MyVideoCapture(self.video_source)
# Create a canvas that can fit the above video source size
self.canvas = tkinter.Canvas(window, width = self.vid.width, height = self.vid.height)
self.canvas.pack()
# Button that lets the user take a snapshot
self.btn_snapshot=tkinter.Button(window, text="Snapshot", width=50, command=self.snapshot)
self.btn_snapshot.pack(anchor=tkinter.CENTER, expand=True)
# After it is called once, the update method will be automatically called every delay milliseconds
self.delay = 15
self.update()
self.window.mainloop()
def snapshot(self):
# Get a frame from the video source
ret, frame = self.vid.get_frame()
if ret:
cv2.imwrite("frame-" + time.strftime("%d-%m-%Y-%H-%M-%S") + ".jpg", cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
def update(self):
# Get a frame from the video source
ret, frame = self.vid.get_frame()
if ret:
self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))
self.canvas.create_image(0, 0, image = self.photo, anchor = tkinter.NW)
self.window.after(self.delay, self.update)
class MyVideoCapture:
def __init__(self, video_source=0):
# Open the video source
self.vid = cv2.VideoCapture(video_source)
if not self.vid.isOpened():
raise ValueError("Unable to open video source", video_source)
# Get video source width and height
self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
def get_frame(self):
if self.vid.isOpened():
ret, frame = self.vid.read()
if ret:
# Return a boolean success flag and the current frame converted to BGR
return (ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
else:
return (ret, None)
else:
return (ret, None)
# Release the video source when the object is destroyed
def __del__(self):
if self.vid.isOpened():
self.vid.release()
# Create a window and pass it to the Application object
App(tkinter.Tk(), "Tkinter and OpenCV")

Categories