chiark / gitweb /
Handle webcam support a bit better, show an error when no webcam is found, and not...
[cura.git] / Cura / gui / util / webcam.py
1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
3
4 import os
5 import glob
6 import subprocess
7 import platform
8
9 import wx
10
11 from Cura.util import profile
12 from Cura.util.resources import getPathForImage
13
14 try:
15         #Try to find the OpenCV library for video capture.
16         from opencv import cv
17         from opencv import highgui
18 except:
19         cv = None
20
21 try:
22         #Use the vidcap library directly from the VideoCapture package. (Windows only)
23         #       http://videocapture.sourceforge.net/
24         # We're using the binary interface, not the python interface, so we don't depend on PIL
25         import vidcap as win32vidcap
26 except:
27         win32vidcap = None
28
29 def hasWebcamSupport():
30         if cv is None and win32vidcap is None:
31                 return False
32         if not os.path.exists(getFFMPEGpath()):
33                 return False
34         return True
35
36
37 def getFFMPEGpath():
38         if platform.system() == "Windows":
39                 return os.path.normpath(os.path.join(os.path.split(__file__)[0], "../../ffmpeg.exe"))
40         elif os.path.exists('/usr/bin/ffmpeg'):
41                 return '/usr/bin/ffmpeg'
42         return os.path.normpath(os.path.join(os.path.split(__file__)[0], "../../ffmpeg"))
43
44
45 class webcam(object):
46         def __init__(self):
47                 self._cam = None
48                 self._cameraList = None
49                 self._activeId = -1
50                 self._overlayImage = wx.Bitmap(getPathForImage('cura-overlay.png'))
51                 self._overlayUltimaker = wx.Bitmap(getPathForImage('ultimaker-overlay.png'))
52                 self._doTimelapse = False
53                 self._bitmap = None
54
55                 #open the camera and close it to check if we have a camera, then open the camera again when we use it for the
56                 # first time.
57                 cameraList = []
58                 tryNext = True
59                 self._camId = 0
60                 while tryNext:
61                         tryNext = False
62                         self._openCam()
63                         if self._cam is not None:
64                                 cameraList.append(self._cam.getdisplayname())
65                                 tryNext = True
66                                 del self._cam
67                                 self._cam = None
68                                 self._camId += 1
69                 self._camId = 0
70                 self._activeId = -1
71                 self._cameraList = cameraList
72
73         def hasCamera(self):
74                 return len(self._cameraList) > 0
75
76         def listCameras(self):
77                 return self._cameraList
78
79         def setActiveCamera(self, cameraIdx):
80                 self._camId = cameraIdx
81
82         def _openCam(self):
83                 if self._cameraList is not None and self._camId >= len(self._cameraList):
84                         return False
85                 if self._cam is not None:
86                         if self._activeId != self._camId:
87                                 del self._cam
88                                 self._cam = None
89                         else:
90                                 return True
91
92                 self._activeId = self._camId
93                 if cv is not None:
94                         self._cam = highgui.cvCreateCameraCapture(self._camId)
95                 elif win32vidcap is not None:
96                         try:
97                                 self._cam = win32vidcap.new_Dev(self._camId, False)
98                         except:
99                                 pass
100                 return self._cam is not None
101
102         def propertyPages(self):
103                 if cv is not None:
104                         #TODO Make an OpenCV property page
105                         return []
106                 elif win32vidcap is not None:
107                         return ['Image properties', 'Format properties']
108
109         def openPropertyPage(self, pageType=0):
110                 if not self._openCam():
111                         return
112                 if cv is not None:
113                         pass
114                 elif win32vidcap is not None:
115                         if pageType == 0:
116                                 self._cam.displaycapturefilterproperties()
117                         else:
118                                 del self._cam
119                                 self._cam = None
120                                 tmp = win32vidcap.new_Dev(0, False)
121                                 tmp.displaycapturepinproperties()
122                                 self._cam = tmp
123
124         def takeNewImage(self, withOverlay = True):
125                 if not self._openCam():
126                         return
127                 if cv is not None:
128                         frame = cv.QueryFrame(self._cam)
129                         cv.CvtColor(frame, frame, cv.CV_BGR2RGB)
130                         bitmap = wx.BitmapFromBuffer(frame.width, frame.height, frame.imageData)
131                 elif win32vidcap is not None:
132                         buffer, width, height = self._cam.getbuffer()
133                         try:
134                                 wxImage = wx.EmptyImage(width, height)
135                                 wxImage.SetData(buffer[::-1])
136                                 wxImage = wxImage.Mirror()
137                                 if self._bitmap is not None:
138                                         del self._bitmap
139                                 bitmap = wxImage.ConvertToBitmap()
140                                 del wxImage
141                                 del buffer
142                         except:
143                                 pass
144
145                 if withOverlay:
146                         dc = wx.MemoryDC()
147                         dc.SelectObject(bitmap)
148                         dc.DrawBitmap(self._overlayImage, bitmap.GetWidth() - self._overlayImage.GetWidth() - 5, 5, True)
149                         if profile.getMachineSetting('machine_type').startswith('ultimaker'):
150                                 dc.DrawBitmap(self._overlayUltimaker, (bitmap.GetWidth() - self._overlayUltimaker.GetWidth()) / 2,
151                                         bitmap.GetHeight() - self._overlayUltimaker.GetHeight() - 5, True)
152                         dc.SelectObject(wx.NullBitmap)
153
154                 self._bitmap = bitmap
155
156                 if self._doTimelapse:
157                         filename = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap",
158                                 "__tmp_snap_%04d.jpg" % (self._snapshotCount)))
159                         self._snapshotCount += 1
160                         bitmap.SaveFile(filename, wx.BITMAP_TYPE_JPEG)
161
162                 return self._bitmap
163
164         def getLastImage(self):
165                 return self._bitmap
166
167         def startTimelapse(self, filename):
168                 if not self._openCam():
169                         return
170                 self._cleanTempDir()
171                 self._timelapseFilename = filename
172                 self._snapshotCount = 0
173                 self._doTimelapse = True
174                 print "startTimelapse"
175
176         def endTimelapse(self):
177                 if self._doTimelapse:
178                         ffmpeg = getFFMPEGpath()
179                         basePath = os.path.normpath(
180                                 os.path.join(os.path.split(__file__)[0], "../__tmp_snap", "__tmp_snap_%04d.jpg"))
181                         subprocess.call(
182                                 [ffmpeg, '-r', '12.5', '-i', basePath, '-vcodec', 'mpeg2video', '-pix_fmt', 'yuv420p', '-r', '25', '-y',
183                                  '-b:v', '1500k', '-f', 'vob', self._timelapseFilename])
184                 self._doTimelapse = False
185
186         def _cleanTempDir(self):
187                 basePath = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap"))
188                 try:
189                         os.makedirs(basePath)
190                 except:
191                         pass
192                 for filename in glob.iglob(basePath + "/*.jpg"):
193                         os.remove(filename)