I am trying to save a webcam video stream using opencv.For that i have used a wxpython GUI window and have button to record.
import wx
import vlc
import time
import os
import user
import cv2, cv
import datetime
class MainWindow(wx.Panel):
def __init__(self, parent,capture):
wx.Panel.__init__(self, parent)
mainSizer = wx.BoxSizer(wx.VERTICAL)
# video
videoWarper = wx.StaticBox(self,size=(640,480))
videoBoxSizer = wx.StaticBoxSizer(videoWarper, wx.VERTICAL)
videoFrame = wx.Panel(self, -1,size=(640,480))
capture = ShowCapture(videoFrame, capture)
videoBoxSizer.Add(videoFrame,0)
mainSizer.Add(videoBoxSizer,0)
parent.Centre()
self.Show()
self.SetSizerAndFit(mainSizer)
# Panels
# The first panel holds the video and it's all black
self.videopanel = wx.Panel(self, -1)
self.videopanel.SetBackgroundColour(wx.BLACK)
# The second panel holds controls
ctrlpanel = wx.Panel(self, -1 )
self.timeslider = wx.Slider(ctrlpanel, -1, 0, 0, 1000)
self.timeslider.SetRange(0, 1000)
record = wx.Button(ctrlpanel, label="Record")
# Bind controls to events
self.Bind(wx.EVT_BUTTON, self.OnRecord, record)
# Give a pretty layout to the controls
ctrlbox = wx.BoxSizer(wx.VERTICAL)
box = wx.BoxSizer(wx.HORIZONTAL)
# box contains some buttons
box.Add(record)
# Merge box to the ctrlsizer
ctrlbox.Add(box, flag=wx.EXPAND, border=10)
ctrlpanel.SetSizer(ctrlbox)
# Put everything togheter
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(ctrlpanel, flag=wx.EXPAND | wx.BOTTOM | wx.TOP, border=10)
self.SetSizer(sizer)
self.SetMinSize((350, 300))
# VLC player controls
self.Instance = vlc.Instance()
self.player = self.Instance.media_player_new()
# -------begin capturing and saving video
def OnRecord(self, evt):
capture = cv.CaptureFromCAM(0)
fps = 9
size = (640, 480)
writer = cv.CreateVideoWriter('video.avi', cv.CV_FOURCC('D', 'I', 'V', 'X'), fps, size);
i = 0
started = datetime.datetime.now()
while i < 300:
i += 1
frame = cv.QueryFrame(capture)
frame2 = cv.CreateImage(size, 8, 3)
cv.CvtColor(frame, frame2, cv.CV_RGB2XYZ)
cv.WriteFrame(writer, frame2)
# cv.WaitKey(1000/fps)
class ShowCapture(wx.Panel):
def __init__(self, parent, capture, fps=24):
wx.Panel.__init__(self, parent, wx.ID_ANY, (0,0), (640,480))
self.capture = capture
ret, frame = self.capture.read()
height, width = frame.shape[:2]
parent.SetSize((width, height))
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
self.bmp = wx.BitmapFromBuffer(width, height, frame)
self.timer = wx.Timer(self)
self.timer.Start(1000./fps)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_TIMER, self.NextFrame)
def OnPaint(self, evt):
dc = wx.BufferedPaintDC(self)
dc.DrawBitmap(self.bmp, 0, 0)
def NextFrame(self, event):
ret, frame = self.capture.read()
if ret:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
self.bmp.CopyFromBuffer(frame)
self.Refresh()
capture = cv2.VideoCapture(0)
app = wx.App(False)
frame = wx.Frame(None, title='CamGUI')
panel = MainWindow(frame,capture)
frame.Show()
app.MainLoop()
I got my webcam stream on GUI window but when i press the record button it throws me an Error which look like this
Traceback (most recent call last):
File "/home/pi/Record_test.py", line 77, in OnRecord
cv.CvtColor(frame, frame2, cv.CV_RGB2XYZ)
cv2.error: scn == 3 || scn == 4
What might be the problem?
Related
This code successfully draws the SVG onto the screen. However, when I resize the window, the original image stays superimposed over the screen, while it redraws it underneath.
Before Resizing:
After Maximizing:
It's especially noticable if you drag to resize
# import igraph # either uncomment and install igraph or provide output.svg in the same dir
import wx
import wx.svg
class NNGui(wx.Frame):
def __init__(self, parent, title):
super().__init__(parent, title=title, size=(800, 600))
self.panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
# main graphics box
self.screen = MainScreen(self.panel)
vbox.Add(self.screen, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)
self.Bind(wx.EVT_SIZE, self.on_resize)
self.Bind(wx.EVT_MAXIMIZE, self.on_resize)
# command box
self.cmd_box = wx.TextCtrl(self.panel, style=wx.TE_PROCESS_ENTER | wx.TE_PROCESS_TAB)
vbox.Add(self.cmd_box, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=10)
self.cmd_box.Bind(wx.EVT_CHAR, self.do_char)
self.panel.SetSizer(vbox)
self.Layout()
self.Centre()
def do_char(self, e):
# handle keypresses
e.Skip()
def on_resize(self, e):
print('resize!')
# self.screen = MainScreen(self.panel)
self.screen.Refresh() # thank you Rolf-of-Saxony
# self.panel.Refresh()
# self.Refresh()
self.screen.Update()
# self.panel.Update()
# self.Update()
e.Skip()
class MainScreen(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
self.img = wx.svg.SVGimage.CreateFromFile('output.svg')
self.Bind(wx.EVT_PAINT, self.on_paint)
def on_paint(self, e):
print('screen painted!')
dc = wx.PaintDC(self)
dc.SetBackground(wx.Brush('black'))
dc.Clear()
dc_dim = min(self.Size.width, self.Size.height)
img_dim = min(self.img.width, self.img.height)
scale = dc_dim / img_dim
width = int(self.img.width * scale)
height = int(self.img.height * scale)
# ctx = wx.GraphicsContext.Create(dc)
# self.img.RenderToGC(ctx, scale)
bmp = self.img.ConvertToBitmap(scale=scale, width=width, height=height)
px_to_center = int((self.Size.width - width) / 2)
dc.DrawBitmap(bmp, px_to_center, 0)
e.Skip()
class NNGraph:
def __init__(self):
self.g = igraph.Graph.GRG(50, 0.2)
def write_svg(self, filename='output.svg'):
assert filename.endswith('.svg')
igraph.plot(self.g, filename)
def main():
# graph = NNGraph() # either uncomment and install igraph or provide output.svg in the same dir
# graph.write_svg()
app = wx.App()
frame = NNGui(None, title='NeuronicNodes')
frame.Show()
app.MainLoop()
if __name__ == '__main__':
main()
I've tried applying Update() to several panels, bound events for EVT_SIZE, EVT_PAINT, tried recreating the panel... I'm not sure what I'm missing.
Below I have an example of my code, you can drag and drop an image from your desktop into the window and it will change the image in the app. I want to only drag/drop images into the image, not the border around the image in the window.
In another words, you can drag/drop images anywhere in the window, but I only want you to be able to drag/drop images on top of the image.
import wx
from wx import *
import wx.lib.statbmp as SB
from PIL import Image
from pubsub import pub
import wx.lib.inspection #inspection tool
#=======================================================================#
# DRAG/DROP
#=======================================================================#
PhotoMaxSize = 485
class DropTarget(wx.FileDropTarget):
def __init__(self, widget):
wx.FileDropTarget.__init__(self)
self.widget = widget
def OnDropFiles(self, x, y, filenames):
pub.sendMessage('dnd', filepath=filenames[0])
return True
#=======================================================================#
# FRAME
#=======================================================================#
class PhotoCtrl(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Learning GUI', size=(800,500), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER)
self.SetBackgroundColour('#1a1a1a')
self.panel = MainPanel(self)
self.main_sizer = wx.BoxSizer()
self.main_sizer.Add(self.panel, 0, wx.EXPAND, 10)
self.Show()
#=======================================================================#
# DRAG/DROP IMAGE PANEL
#=======================================================================#
class MainPanel(wx.Panel):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.parent = parent
bg3 = wx.Image('bg3.jpg', wx.BITMAP_TYPE_ANY)
img = wx.Image(bg3)
self.image_ctrl = SB.GenStaticBitmap(
self, wx.ID_ANY, wx.Bitmap(img))
file_drop_target = DropTarget(self)
self.SetDropTarget(file_drop_target)
pub.subscribe(self.update_image_on_dnd, 'dnd')
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.AddStretchSpacer(5)
sizer.Add(self.image_ctrl, 0, wx.ALIGN_CENTER, 10)
sizer.AddStretchSpacer(1)
self.SetSizer(sizer)
def update_image_on_dnd(self, filepath):
self.on_view(filepath=filepath)
def on_view(self, filepath):
img = wx.Image(filepath, wx.BITMAP_TYPE_ANY)
W = img.GetWidth()
H = img.GetHeight()
if W > H:
new_w = PhotoMaxSize
new_h = PhotoMaxSize * H / W
else:
new_h = PhotoMaxSize
new_w = PhotoMaxSize * W / H
img = img.Scale(new_w, new_h)
self.image_ctrl.SetBitmap(wx.Bitmap(img))
#self.Fit() #removing this makes window size not change when dropping pic in
self.parent.Fit()
#=======================================================================#
# END
#=======================================================================#
if __name__ == '__main__':
app = wx.App()
frame = PhotoCtrl()
wx.lib.inspection.InspectionTool().Show() #inspection tool
app.MainLoop()
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")
I want something similar like this image and this is same layout which I supposed to want.
And with additional note,I want to generate a graph based on the video file timings.For Eg. 10 sec this graph should be generated and after 20 sec another graph should be generated.
Is this possible
I wanted to show that it's even possible to update the plot for each frame at video rate using wx pyhotn.
This example will calculate the average pixel intensity along x-axis and update the plot for every frame. Since you want to update every 10 sec, you will need some modification. This Clip (Jenny Mayhem) is taken from https://www.youtube.com/watch?v=cOcgOnBe5Ag
import cv2
import numpy as np
import matplotlib
matplotlib.use('WXAgg') # not sure if this is needed
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import wx
class VideoPanel(wx.Panel):
def __init__(self, parent, size):
wx.Panel.__init__(self, parent, -1, size=size)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.parent = parent
self.SetDoubleBuffered(True)
def OnPaint(self, event):
dc = wx.BufferedPaintDC(self)
dc.Clear()
if self.parent.bmp:
dc.DrawBitmap(self.parent.bmp,0,0)
class MyFrame(wx.Frame):
def __init__(self, fp):
wx.Frame.__init__(self, None)
self.bmp = None
self.cap = cv2.VideoCapture(fp)
ret, frame = self.cap.read()
h,w,c = frame.shape
print w,h,c
videopPanel = VideoPanel(self, (w,h))
self.videotimer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnUpdateVidoe, self.videotimer)
self.videotimer.Start(1000/30.0)
self.graph = Figure() # matplotlib figure
plottPanel = FigureCanvas(self, -1, self.graph)
self.ax = self.graph.add_subplot(111)
y = frame.mean(axis=0).mean(axis=1)
self.line, = self.ax.plot(y)
self.ax.set_xlim([0,w])
self.ax.set_ylim([0,255])
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(videopPanel)
sizer.Add(plottPanel)
self.SetSizer(sizer)
self.Fit()
self.Show(True)
def OnUpdateVidoe(self, event):
ret, frame = self.cap.read()
if ret:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img_buf = wx.ImageFromBuffer(frame.shape[1], frame.shape[0], frame)
self.bmp = wx.BitmapFromImage(img_buf)
# modify this part to update every 10 sec etc...
# right now, it's realtime update (every frame)
y = frame.mean(axis=0).mean(axis=1)
self.line.set_ydata(y)
self.graph.canvas.draw()
self.Refresh()
if __name__ == '__main__':
fp = "Jenny Mayhem and The Fuzz Orchestrator - Gypsy Gentleman (Live at the Lodge on Queen).mp4"
app = wx.App(0)
myframe = MyFrame(fp)
app.MainLoop()
I have read all three or four current threads on this subject that are on the internet, and so far none accurately answer the question.
I am fairly new to wxPython, although I have some experience with FLTK. I am new to OpenCV.
I am attempting to capture an image from a webcam with openCV and paint that image into wxPython. I have had limited success (I can get an image and paint it, but it's faint and not aligned properly). I can confirm that my webcam and openCV are working on their own, because sample code like this works as expected.
Here is an example of my latest effort, which I've cobbled together from the internet and my own efforts with opencv2.
import wx
import cv2
class viewWindow(wx.Frame):
imgSizer = (480,360)
def __init__(self, parent, title="View Window"):
super(viewWindow,self).__init__(parent)
self.pnl = wx.Panel(self)
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.image = wx.EmptyImage(self.imgSizer[0],self.imgSizer[1])
self.imageBit = wx.BitmapFromImage(self.image)
self.staticBit = wx.StaticBitmap(self.pnl,wx.ID_ANY,
self.imageBit)
self.vbox.Add(self.staticBit)
self.pnl.SetSizer(self.vbox)
self.timex = wx.Timer(self, wx.ID_OK)
self.timex.Start(1000/12)
self.Bind(wx.EVT_TIMER, self.redraw, self.timex)
self.capture = cv2.VideoCapture(0)
self.SetSize(self.imgSizer)
self.Show()
def redraw(self,e):
ret, frame = self.capture.read()
#print('tick')
self.imageBit = wx.BitmapFromImage(self.image)
self.staticBit = wx.StaticBitmap(self.pnl,
wx.ID_ANY, self.imageBit)
self.Refresh()
def main():
app = wx.PySimpleApp()
frame = viewWindow(None)
frame.Center()
frame.Show()
app.MainLoop()
if __name__ == '__main__':
main()
OpenCV is not a hard requirement (I'm open to other options as long as the solution is cross-platform. If I'm not mistaken, Gstreamer is not cross platform, and PyGame has been difficult to embed in wxPython, but I'm open to ideas).
wxPython is a hard requirement.
if OpenCV is not a hard requirement
try libvlc and its python bindigs
https://wiki.videolan.org/Python_bindings
you can try this modified code, it should show the webcam image from opencv2 into your wx python environment:
import wx
import cv2
class viewWindow(wx.Frame):
def __init__(self, parent, title="View Window"):
# super(viewWindow,self).__init__(parent)
wx.Frame.__init__(self, parent)
self.imgSizer = (480, 360)
self.pnl = wx.Panel(self)
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.image = wx.EmptyImage(self.imgSizer[0],self.imgSizer[1])
self.imageBit = wx.BitmapFromImage(self.image)
self.staticBit = wx.StaticBitmap(self.pnl, wx.ID_ANY, self.imageBit)
self.vbox.Add(self.staticBit)
self.capture = cv2.VideoCapture(0)
ret, self.frame = self.capture.read()
if ret:
self.height, self.width = self.frame.shape[:2]
self.bmp = wx.BitmapFromBuffer(self.width, self.height, self.frame)
self.timex = wx.Timer(self)
self.timex.Start(1000./24)
self.Bind(wx.EVT_TIMER, self.redraw)
self.SetSize(self.imgSizer)
else:
print "Error no webcam image"
self.pnl.SetSizer(self.vbox)
self.vbox.Fit(self)
self.Show()
def redraw(self,e):
ret, self.frame = self.capture.read()
if ret:
self.frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
self.bmp.CopyFromBuffer(self.frame)
self.staticBit.SetBitmap(self.bmp)
self.Refresh()
def main():
app = wx.PySimpleApp()
frame = viewWindow(None)
frame.Center()
frame.Show()
app.MainLoop()
if __name__ == '__main__':
main()