├── .gitignore ├── CMakeLists.txt ├── README.txt ├── camera-1.0-0.rockspec ├── camera-1.1-0.rockspec ├── examples ├── test-fps.lua ├── test-hex.lua ├── test-multi.lua ├── test-one.lua ├── test-two.lua └── test.lua ├── init.lua ├── macos ├── CMakeLists.txt ├── camera.h ├── camera.m └── init.lua ├── opencv ├── CMakeLists.txt ├── init.lua ├── opencv.c └── opencv.h └── video4linux ├── CMakeLists.txt ├── init.lua └── v4l.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.6 FATAL_ERROR) 2 | CMAKE_POLICY(VERSION 2.6) 3 | IF(LUAROCKS_PREFIX) 4 | MESSAGE(STATUS "Installing Torch through Luarocks") 5 | STRING(REGEX REPLACE "(.*)lib/luarocks/rocks.*" "\\1" CMAKE_INSTALL_PREFIX "${LUAROCKS_PREFIX}") 6 | MESSAGE(STATUS "Prefix inferred from Luarocks: ${CMAKE_INSTALL_PREFIX}") 7 | ENDIF() 8 | FIND_PACKAGE(Torch REQUIRED) 9 | 10 | if (UNIX AND NOT APPLE) 11 | add_subdirectory (opencv) 12 | add_subdirectory (video4linux) 13 | endif (UNIX AND NOT APPLE) 14 | 15 | if (APPLE) 16 | add_subdirectory (opencv) 17 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 18 | add_subdirectory (macos) 19 | endif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 20 | endif (APPLE) 21 | 22 | if (NOT UNIX) 23 | message (ERROR "This package only builds on Unix platforms") 24 | endif (NOT UNIX) 25 | 26 | install_files(/lua/camera init.lua) 27 | 28 | SET(src) 29 | SET(luasrc init.lua) 30 | ADD_TORCH_PACKAGE(camera "${src}" "${luasrc}" "Image Processing") 31 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | DEPENDENCIES: 2 | MacOS: Install OpenCV 2.X: http://opencv.willowgarage.com/ 3 | Linux: None 4 | 5 | All: Torch7 (follow instructions here: www.torch.ch) 6 | 7 | INSTALL: 8 | $ luarocks install camera 9 | 10 | STATE: 11 | MacOS: working on all MacOS builds, using OpenCV (wrapper from Jordan Bates) 12 | Linux: working all right, using raw video4linux2 (wrapper from Clement Farabet) 13 | 14 | USE: 15 | $ torch 16 | > require 'camera' 17 | > camera.testme() -- a simple grabber+display 18 | > cam = image.Camera() -- create the camera grabber 19 | > frame = cam:forward() -- return the next frame available 20 | > cam:stop() -- release the camera 21 | > image.display(frame) -- display frame 22 | -------------------------------------------------------------------------------- /camera-1.0-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "camera" 2 | version = "1.0-0" 3 | 4 | source = { 5 | url = "git://github.com/clementfarabet/lua---camera", 6 | tag = "1.0-0" 7 | } 8 | 9 | description = { 10 | summary = "A simple wrapper package to give torch access to a webcam", 11 | detailed = [[ 12 | Uses OpenCV on MacOS and video4linux2 on Linux to proved the low level access to the camera hardware. 13 | ]], 14 | homepage = "https://github.com/clementfarabet/lua---camera", 15 | license = "BSD" 16 | } 17 | 18 | dependencies = { 19 | "torch >= 7.0", 20 | "xlua >= 1.0", 21 | "sys >= 1.0", 22 | "image >= 1.0.1" 23 | } 24 | 25 | build = { 26 | type = "command", 27 | build_command = [[ 28 | cmake -E make_directory build; 29 | cd build; 30 | cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$(LUA_BINDIR)/.." -DCMAKE_INSTALL_PREFIX="$(PREFIX)"; 31 | $(MAKE) 32 | ]], 33 | install_command = "cd build && $(MAKE) install" 34 | } 35 | -------------------------------------------------------------------------------- /camera-1.1-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "camera" 2 | version = "1.1-0" 3 | 4 | source = { 5 | url = "git://github.com/clementfarabet/lua---camera", 6 | } 7 | 8 | description = { 9 | summary = "A simple wrapper package to give torch access to a webcam", 10 | detailed = [[ 11 | Uses OpenCV on MacOS and video4linux2 on Linux to proved the low level access to the camera hardware. 12 | ]], 13 | homepage = "https://github.com/clementfarabet/lua---camera", 14 | license = "BSD" 15 | } 16 | 17 | dependencies = { 18 | "torch >= 7.0", 19 | "xlua >= 1.0", 20 | "sys >= 1.0", 21 | "image >= 1.0.1" 22 | } 23 | 24 | build = { 25 | type = "command", 26 | build_command = [[ 27 | cmake -E make_directory build; 28 | cd build; 29 | cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$(LUA_BINDIR)/.." -DCMAKE_INSTALL_PREFIX="$(PREFIX)"; 30 | $(MAKE) 31 | ]], 32 | install_command = "cd build && $(MAKE) install" 33 | } 34 | -------------------------------------------------------------------------------- /examples/test-fps.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env torch 2 | require 'torch' 3 | require 'sys' 4 | require 'camera' 5 | require 'xlua' 6 | 7 | op = xlua.OptionParser('%prog [options]') 8 | op:option{'-dev','--device', action='store',dest='devvideo', 9 | help='number of the /dev/video* device from which you want to capture', 10 | default=0} 11 | op:option{'-w','--width', action='store', dest='width', 12 | help='width of frames you want to capture', default=640} 13 | op:option{'-h','--height', action='store', dest='height', 14 | help='height of frames you want to capture', default=480} 15 | op:option{'-r','--fps', action='store', dest='fps', 16 | help='fps at which you want to capture', default=15} 17 | 18 | opt, args = op:parse() 19 | op:summarize() 20 | 21 | dev = tonumber(opt.devvideo) 22 | width = tonumber(opt.width) 23 | height = tonumber(opt.height) 24 | fps = tonumber(opt.fps) 25 | 26 | camera1 = image.Camera{idx=dev,width=width,height=height,fps=fps} 27 | 28 | a1 = camera1:forward() 29 | f = 1 30 | 31 | while true do 32 | sys.tic() 33 | a1 = camera1:forward() 34 | f = f + 1 35 | print("FPS: ".. 1/sys.toc()) 36 | end 37 | -------------------------------------------------------------------------------- /examples/test-hex.lua: -------------------------------------------------------------------------------- 1 | require 'xlua' 2 | require 'sys' 3 | require 'camera' 4 | width = 320 5 | height = 240 6 | fps = 30 7 | dir = 'test_vid_hex_nowide' 8 | 9 | sys.execute(string.format('mkdir -p %s',dir)) 10 | 11 | camera1 = image.Camera{idx=1,width=width,height=height,fps=fps} 12 | camera2 = image.Camera{idx=2,width=width,height=height,fps=fps} 13 | camera3 = image.Camera{idx=3,width=width,height=height,fps=fps} 14 | camera4 = image.Camera{idx=4,width=width,height=height,fps=fps} 15 | camera5 = image.Camera{idx=5,width=width,height=height,fps=fps} 16 | 17 | a1 = camera1:forward() 18 | a2 = camera2:forward() 19 | a3 = camera3:forward() 20 | a4 = camera4:forward() 21 | a5 = camera5:forward() 22 | win = image.display{win=win,image={a1,a2,a3,a4,a5}} 23 | f = 1 24 | 25 | frame = torch.Tensor(3,240,1600) 26 | 27 | while true do 28 | sys.tic() 29 | a1 = camera1:forward() 30 | a2 = camera2:forward() 31 | a3 = camera3:forward() 32 | a4 = camera4:forward() 33 | a5 = camera5:forward() 34 | frame:narrow(3,1,320):copy(a5) 35 | frame:narrow(3,320,320):copy(a4) 36 | frame:narrow(3,320*2,320):copy(a3) 37 | frame:narrow(3,320*3,320):copy(a2) 38 | frame:narrow(3,320*4,320):copy(a1) 39 | image.display{win=win,image=frame} 40 | image.savePNG(string.format("%s/frame_%05d.png",dir,f),frame) 41 | for i = 1,5 do 42 | image.savePNG(string.format("%s/frame_%05d_%d.png",dir,f,i),frame) 43 | end 44 | f = f + 1 45 | print("FPS: ".. 1/sys.toc()) 46 | end -------------------------------------------------------------------------------- /examples/test-multi.lua: -------------------------------------------------------------------------------- 1 | require 'xlua' 2 | require 'camera' 3 | width = 320 4 | height = 240 5 | 6 | camera1 = image.Camera{idx=1,width=width,height=height} 7 | camera2 = image.Camera{idx=2,width=width,height=height} 8 | camera3 = image.Camera{idx=3,width=width,height=height} 9 | camera4 = image.Camera{idx=4,width=width,height=height} 10 | 11 | a1 = camera1:forward() 12 | a2 = camera2:forward() 13 | a3 = camera3:forward() 14 | a4 = camera4:forward() 15 | win = image.display{win=win,image={a1,a2,a3,a4}} 16 | 17 | while true do 18 | sys.tic() 19 | a1 = camera1:forward() 20 | a2 = camera2:forward() 21 | a3 = camera3:forward() 22 | a4 = camera4:forward() 23 | image.display{win=win,image={a1,a2,a3,a4}} 24 | print("FPS: ".. 1/sys.toc()) 25 | end -------------------------------------------------------------------------------- /examples/test-one.lua: -------------------------------------------------------------------------------- 1 | require 'torch' 2 | require 'sys' 3 | require 'camera' 4 | width = 1600 5 | height = 896 6 | fps = 1 7 | dir = "demo_test" 8 | 9 | sys.execute(string.format('mkdir -p %s',dir)) 10 | 11 | camera1 = image.Camera{idx=1,width=width,height=height,fps=fps} 12 | 13 | a1 = camera1:forward() 14 | win = image.display{win=win,image={a1}} 15 | f = 1 16 | 17 | while true do 18 | sys.tic() 19 | a1 = camera1:forward() 20 | image.display{win=win,image={a1}} 21 | image.savePNG(string.format("%s/frame_1_%05d.png",dir,f),a1) 22 | f = f + 1 23 | print("FPS: ".. 1/sys.toc()) 24 | end -------------------------------------------------------------------------------- /examples/test-two.lua: -------------------------------------------------------------------------------- 1 | require 'xlua' 2 | require 'camera' 3 | width = 1920 4 | height = 1080 5 | fps = 30 6 | dir = 'test_vid_two' 7 | 8 | sys.execute(string.format('mkdir -p %s',dir)) 9 | 10 | camera1 = image.Camera{idx=1,width=width,height=height,fps=fps} 11 | camera2 = image.Camera{idx=2,width=width,height=height,fps=fps} 12 | 13 | a1 = camera1:forward() 14 | a2 = camera2:forward() 15 | win = image.display{win=win,image={a1,a2}} 16 | 17 | frame = torch.Tensor(3,height,2*width) 18 | f = 1 19 | 20 | while true do 21 | sys.tic() 22 | a1 = camera1:forward() 23 | a2 = camera2:forward() 24 | 25 | frame:narrow(3,1,width):copy(a1) 26 | frame:narrow(3,width,width):copy(a2) 27 | 28 | image.display{win=win,image=frame} 29 | -- image.savePNG(string.format("%s/frame_%05d.png",dir,f),frame) 30 | f = f + 1 31 | print("FPS: ".. 1/sys.toc()) 32 | end -------------------------------------------------------------------------------- /examples/test.lua: -------------------------------------------------------------------------------- 1 | require 'image' 2 | require 'cammacos' 3 | 4 | camera = image.Camera{idx={0,1,2,3,4}, width=320, height=240} 5 | 6 | for i = 1,500 do 7 | sys.tic() 8 | a = camera:forward() 9 | sys.toc(true) 10 | d = image.display{image=a, win=d, zoom=1} 11 | end 12 | 13 | camera:stop() 14 | -------------------------------------------------------------------------------- /init.lua: -------------------------------------------------------------------------------- 1 | 2 | ---------------------------------- 3 | -- dependencies 4 | ---------------------------------- 5 | require 'sys' 6 | require 'xlua' 7 | 8 | ---------------------------------- 9 | -- load camera driver based on OS 10 | ---------------------------------- 11 | if useOpenCV then 12 | if not xlua.require 'camopencv' then 13 | xlua.error('failed to load camopencv wrapper: verify that camopencv is installed') 14 | end 15 | elseif sys.OS == 'linux' then 16 | if not xlua.require 'v4l' then 17 | xlua.error('failed to load video4linux wrapper: verify that you have v4l2 libs') 18 | end 19 | elseif sys.OS == 'macos' then 20 | if not xlua.require 'camopencv' then 21 | xlua.error('failed to load camopencv wrapper: verify that camopencv is installed') 22 | end 23 | else 24 | xlua.error('no camera driver available for your OS, sorry :-(') 25 | end 26 | 27 | ---------------------------------- 28 | -- package a little demo 29 | ---------------------------------- 30 | camera = {} 31 | camera.testme = function() 32 | require 'qtwidget' 33 | print '--------------------------------------------------' 34 | print 'grabbing frames from your camera for about 10secs' 35 | local cam = image.Camera{} 36 | local w = camera._w 37 | local fps = 0 38 | for i = 1,200 do -- ~10 seconds 39 | sys.tic() 40 | local frame = cam:forward() 41 | w = image.display{image=frame, win=w, legend='camera capture ['..fps..'fps]'} 42 | w.window:show() 43 | local t = sys.toc() 44 | if fps == 0 then fps = 1/t end 45 | fps = math.ceil((1/t + fps)/2) 46 | end 47 | cam:stop() 48 | print 'done: create your own frame grabber with image.Camera()' 49 | print '--------------------------------------------------' 50 | camera._w = w 51 | w.window:hide() 52 | return w 53 | end 54 | -------------------------------------------------------------------------------- /macos/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | FIND_LIBRARY(COCOA_LIBRARY Cocoa) 3 | FIND_LIBRARY(CORE_LIBRARY CoreFoundation) 4 | FIND_LIBRARY(QTKIT_LIBRARY QTKit) 5 | FIND_LIBRARY(QUARTZ_LIBRARY Quartz) 6 | 7 | MARK_AS_ADVANCED(COCOA_LIBRARY CORE_LIBRARY QTKIT_LIBRARY QUARTZ_LIBRARY) 8 | SET(EXTRA_LIBS ${COCOA_LIBRARY} ${CORE_LIBRARY} ${QTKIT_LIBRARY} ${QUARTZ_LIBRARY}) 9 | 10 | INCLUDE_DIRECTORIES(${TORCH_INCLUDE_DIR} ${PROJECT_SOURCE_DIR}/macos) 11 | 12 | SET(src camera.m) 13 | SET(luasrc init.lua) 14 | ADD_TORCH_PACKAGE(cammacos "${src}" "${luasrc}" "Image Processing") 15 | SET_TARGET_PROPERTIES(cammacos PROPERTIES COMPILE_FLAGS "-std=c99") 16 | TARGET_LINK_LIBRARIES(cammacos luaT TH ${EXTRA_LIBS}) 17 | -------------------------------------------------------------------------------- /macos/camera.h: -------------------------------------------------------------------------------- 1 | // 2 | // Taken from Robert Harder's page 3 | // 4 | 5 | #import 6 | #import 7 | 8 | #define error(...) fprintf(stderr, __VA_ARGS__) 9 | #define console(...) (!g_quiet && printf(__VA_ARGS__)) 10 | #define verbose(...) (g_verbose && !g_quiet && fprintf(stderr, __VA_ARGS__)) 11 | 12 | BOOL g_verbose = NO; 13 | BOOL g_quiet = NO; 14 | 15 | @interface ImageSnap : NSObject { 16 | 17 | QTCaptureSession *mCaptureSession; 18 | QTCaptureDeviceInput *mCaptureDeviceInput; 19 | QTCaptureDecompressedVideoOutput *mCaptureDecompressedVideoOutput; 20 | CVImageBufferRef mCurrentImageBuffer; 21 | } 22 | 23 | /** 24 | * Returns all attached QTCaptureDevice objects that have video. 25 | * This includes video-only devices (QTMediaTypeVideo) and 26 | * audio/video devices (QTMediaTypeMuxed). 27 | * 28 | * @return autoreleased array of video devices 29 | */ 30 | +(NSArray *)videoDevices; 31 | 32 | /** 33 | * Returns the default QTCaptureDevice object for video 34 | * or nil if none is found. 35 | */ 36 | +(QTCaptureDevice *)defaultVideoDevice; 37 | 38 | /** 39 | * Returns the QTCaptureDevice with the given name 40 | * or nil if the device cannot be found. 41 | */ 42 | +(QTCaptureDevice *)deviceNamed:(NSString *)name; 43 | 44 | /** 45 | * Writes an NSImage to disk, formatting it according 46 | * to the file extension. If path is "-" (a dash), then 47 | * an jpeg representation is written to standard out. 48 | */ 49 | + (BOOL) saveImage:(NSImage *)image toPath: (NSString*)path; 50 | 51 | /** 52 | * Converts an NSImage to raw NSData according to a given 53 | * format. A simple string search is performed for such 54 | * characters as jpeg, tiff, png, and so forth. 55 | */ 56 | +(NSData *)dataFrom:(NSImage *)image asType:(NSString *)format; 57 | 58 | -(id)init; 59 | -(void)dealloc; 60 | 61 | -(BOOL)startSession:(QTCaptureDevice *)device withWidth:(unsigned int)width withHeight:(unsigned int)height; 62 | -(CIImage *)snapshot; 63 | -(void)stopSession; 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /macos/camera.m: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // Description: A wrapper for Mac OS's camera API 3 | // 4 | // Created: January 12, 2012, 10:21AM 5 | // 6 | // Author: Clement Farabet 7 | //============================================================================== 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | @interface ImageSnap() 28 | 29 | - (void)captureOutput:(QTCaptureOutput *)captureOutput 30 | didOutputVideoFrame:(CVImageBufferRef)videoFrame 31 | withSampleBuffer:(QTSampleBuffer *)sampleBuffer 32 | fromConnection:(QTCaptureConnection *)connection; 33 | 34 | @end 35 | 36 | @implementation ImageSnap 37 | 38 | - (id)init{ 39 | self = [super init]; 40 | mCaptureSession = nil; 41 | mCaptureDeviceInput = nil; 42 | mCaptureDecompressedVideoOutput = nil; 43 | mCurrentImageBuffer = nil; 44 | return self; 45 | } 46 | 47 | - (void)dealloc{ 48 | 49 | if( mCaptureSession ) [mCaptureSession release]; 50 | if( mCaptureDeviceInput ) [mCaptureDeviceInput release]; 51 | if( mCaptureDecompressedVideoOutput ) [mCaptureDecompressedVideoOutput release]; 52 | CVBufferRelease(mCurrentImageBuffer); 53 | 54 | [super dealloc]; 55 | } 56 | 57 | 58 | // Returns an array of video devices attached to this computer. 59 | + (NSArray *)videoDevices{ 60 | NSMutableArray *results = [NSMutableArray arrayWithCapacity:3]; 61 | [results addObjectsFromArray:[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo]]; 62 | [results addObjectsFromArray:[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeMuxed]]; 63 | return results; 64 | } 65 | 66 | // Returns the default video device or nil if none found. 67 | + (QTCaptureDevice *)defaultVideoDevice{ 68 | QTCaptureDevice *device = nil; 69 | 70 | device = [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo]; 71 | if( device == nil ){ 72 | device = [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeMuxed]; 73 | } 74 | return device; 75 | } 76 | 77 | // Returns the named capture device or nil if not found. 78 | +(QTCaptureDevice *)deviceNamed:(NSString *)name{ 79 | QTCaptureDevice *result = nil; 80 | 81 | NSArray *devices = [ImageSnap videoDevices]; 82 | for( QTCaptureDevice *device in devices ){ 83 | if ( [name isEqualToString:[device description]] ){ 84 | result = device; 85 | } // end if: match 86 | } // end for: each device 87 | 88 | return result; 89 | } // end 90 | 91 | 92 | // Saves an image to a file or standard out if path is nil or "-" (hyphen). 93 | + (BOOL) saveImage:(NSImage *)image toPath: (NSString*)path{ 94 | 95 | NSString *ext = [path pathExtension]; 96 | NSData *photoData = [ImageSnap dataFrom:image asType:ext]; 97 | 98 | // If path is a dash, that means write to standard out 99 | if( path == nil || [@"-" isEqualToString:path] ){ 100 | NSUInteger length = [photoData length]; 101 | NSUInteger i; 102 | char *start = (char *)[photoData bytes]; 103 | for( i = 0; i < length; ++i ){ 104 | putc( start[i], stdout ); 105 | } // end for: write out 106 | return YES; 107 | } else { 108 | return [photoData writeToFile:path atomically:NO]; 109 | } 110 | 111 | 112 | return NO; 113 | } 114 | 115 | 116 | /** 117 | * Converts an NSImage into NSData. Defaults to jpeg if 118 | * format cannot be determined. 119 | */ 120 | +(NSData *)dataFrom:(NSImage *)image asType:(NSString *)format{ 121 | 122 | NSData *tiffData = [image TIFFRepresentation]; 123 | 124 | NSBitmapImageFileType imageType = NSJPEGFileType; 125 | NSDictionary *imageProps = nil; 126 | 127 | 128 | // TIFF. Special case. Can save immediately. 129 | if( [@"tif" rangeOfString:format options:NSCaseInsensitiveSearch].location != NSNotFound || 130 | [@"tiff" rangeOfString:format options:NSCaseInsensitiveSearch].location != NSNotFound ){ 131 | return tiffData; 132 | } 133 | 134 | // JPEG 135 | else if( [@"jpg" rangeOfString:format options:NSCaseInsensitiveSearch].location != NSNotFound || 136 | [@"jpeg" rangeOfString:format options:NSCaseInsensitiveSearch].location != NSNotFound ){ 137 | imageType = NSJPEGFileType; 138 | imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.9] forKey:NSImageCompressionFactor]; 139 | 140 | } 141 | 142 | // PNG 143 | else if( [@"png" rangeOfString:format options:NSCaseInsensitiveSearch].location != NSNotFound ){ 144 | imageType = NSPNGFileType; 145 | } 146 | 147 | // BMP 148 | else if( [@"bmp" rangeOfString:format options:NSCaseInsensitiveSearch].location != NSNotFound ){ 149 | imageType = NSBMPFileType; 150 | } 151 | 152 | // GIF 153 | else if( [@"gif" rangeOfString:format options:NSCaseInsensitiveSearch].location != NSNotFound ){ 154 | imageType = NSGIFFileType; 155 | } 156 | 157 | NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:tiffData]; 158 | NSData *photoData = [imageRep representationUsingType:imageType properties:imageProps]; 159 | 160 | return photoData; 161 | } // end dataFrom 162 | 163 | /** 164 | * Returns current snapshot or nil if there is a problem 165 | * or session is not started. 166 | */ 167 | -(CIImage *)snapshot{ 168 | verbose( "Taking snapshot...\n"); 169 | 170 | CVImageBufferRef frame = nil; // Hold frame we find 171 | while( frame == nil ){ // While waiting for a frame 172 | 173 | //verbose( "\tEntering synchronized block to see if frame is captured yet..."); 174 | @synchronized(self){ // Lock since capture is on another thread 175 | frame = mCurrentImageBuffer; // Hold current frame 176 | CVBufferRetain(frame); // Retain it (OK if nil) 177 | } // end sync: self 178 | //verbose( "Done.\n" ); 179 | 180 | if( frame == nil ){ // Still no frame? Wait a little while. 181 | [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow: 0.1]]; 182 | } 183 | 184 | } 185 | 186 | // Convert frame to an NSImage 187 | NSCIImageRep *imageRep = [NSCIImageRep imageRepWithCIImage:[CIImage imageWithCVImageBuffer:frame]]; 188 | CIImage *image = [imageRep CIImage]; 189 | [imageRep release]; 190 | 191 | return image; 192 | } 193 | 194 | 195 | 196 | 197 | /** 198 | * Blocks until session is stopped. 199 | */ 200 | -(void)stopSession{ 201 | verbose("Stopping session...\n" ); 202 | 203 | // Make sure we've stopped 204 | while( mCaptureSession != nil ){ 205 | verbose("\tCaptureSession != nil\n"); 206 | 207 | verbose("\tStopping CaptureSession..."); 208 | [mCaptureSession stopRunning]; 209 | verbose("Done.\n"); 210 | 211 | if( [mCaptureSession isRunning] ){ 212 | verbose( "[mCaptureSession isRunning]"); 213 | [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow: 0.1]]; 214 | }else { 215 | verbose( "\tShutting down 'stopSession(..)'" ); 216 | if( mCaptureSession ) [mCaptureSession release]; 217 | if( mCaptureDeviceInput ) [mCaptureDeviceInput release]; 218 | if( mCaptureDecompressedVideoOutput ) [mCaptureDecompressedVideoOutput release]; 219 | 220 | mCaptureSession = nil; 221 | mCaptureDeviceInput = nil; 222 | mCaptureDecompressedVideoOutput = nil; 223 | } // end if: stopped 224 | 225 | } // end while: not stopped 226 | } 227 | 228 | 229 | /** 230 | * Begins the capture session. Frames begin coming in. 231 | */ 232 | -(BOOL)startSession:(QTCaptureDevice *)device 233 | withWidth:(unsigned int)width 234 | withHeight:(unsigned int)height 235 | { 236 | 237 | verbose( "Starting capture session...\n" ); 238 | 239 | if( device == nil ) { 240 | verbose( "\tCannot start session: no device provided.\n" ); 241 | return NO; 242 | } 243 | 244 | NSError *error = nil; 245 | 246 | // If we've already started with this device, return 247 | if( [device isEqual:[mCaptureDeviceInput device]] && 248 | mCaptureSession != nil && 249 | [mCaptureSession isRunning] ){ 250 | return YES; 251 | } // end if: already running 252 | 253 | else if( mCaptureSession != nil ){ 254 | verbose( "\tStopping previous session.\n" ); 255 | [self stopSession]; 256 | } // end if: else stop session 257 | 258 | 259 | // Create the capture session 260 | verbose( "\tCreating QTCaptureSession..." ); 261 | mCaptureSession = [[QTCaptureSession alloc] init]; 262 | verbose( "Done.\n"); 263 | if( ![device open:&error] ){ 264 | error( "\tCould not create capture session.\n" ); 265 | [mCaptureSession release]; 266 | mCaptureSession = nil; 267 | return NO; 268 | } 269 | 270 | 271 | // Create input object from the device 272 | verbose( "\tCreating QTCaptureDeviceInput with %s...", [[device description] UTF8String] ); 273 | mCaptureDeviceInput = [[QTCaptureDeviceInput alloc] initWithDevice:device]; 274 | verbose( "Done.\n"); 275 | if (![mCaptureSession addInput:mCaptureDeviceInput error:&error]) { 276 | error( "\tCould not convert device to input device.\n"); 277 | [mCaptureSession release]; 278 | [mCaptureDeviceInput release]; 279 | mCaptureSession = nil; 280 | mCaptureDeviceInput = nil; 281 | return NO; 282 | } 283 | 284 | // Decompressed video output 285 | verbose( "\tCreating QTCaptureDecompressedVideoOutput..."); 286 | mCaptureDecompressedVideoOutput = [[QTCaptureDecompressedVideoOutput alloc] init]; 287 | [mCaptureDecompressedVideoOutput setDelegate:self]; 288 | 289 | [mCaptureDecompressedVideoOutput setPixelBufferAttributes:[NSDictionary dictionaryWithObjectsAndKeys: 290 | [NSNumber numberWithUnsignedInt:width], (id)kCVPixelBufferWidthKey, 291 | [NSNumber numberWithUnsignedInt:height], (id)kCVPixelBufferHeightKey, 292 | [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32ARGB], (id)kCVPixelBufferPixelFormatTypeKey, 293 | nil]]; 294 | 295 | verbose( "Done.\n" ); 296 | if (![mCaptureSession addOutput:mCaptureDecompressedVideoOutput error:&error]) { 297 | error( "\tCould not create decompressed output.\n"); 298 | [mCaptureSession release]; 299 | [mCaptureDeviceInput release]; 300 | [mCaptureDecompressedVideoOutput release]; 301 | mCaptureSession = nil; 302 | mCaptureDeviceInput = nil; 303 | mCaptureDecompressedVideoOutput = nil; 304 | return NO; 305 | } 306 | 307 | // Clear old image? 308 | verbose("\tEntering synchronized block to clear memory..."); 309 | @synchronized(self){ 310 | if( mCurrentImageBuffer != nil ){ 311 | CVBufferRelease(mCurrentImageBuffer); 312 | mCurrentImageBuffer = nil; 313 | } // end if: clear old image 314 | } // end sync: self 315 | verbose( "Done.\n"); 316 | 317 | [mCaptureSession startRunning]; 318 | verbose("Session started.\n"); 319 | 320 | return YES; 321 | } // end startSession 322 | 323 | 324 | 325 | // This delegate method is called whenever the QTCaptureDecompressedVideoOutput receives a frame 326 | - (void)captureOutput:(QTCaptureOutput *)captureOutput 327 | didOutputVideoFrame:(CVImageBufferRef)videoFrame 328 | withSampleBuffer:(QTSampleBuffer *)sampleBuffer 329 | fromConnection:(QTCaptureConnection *)connection 330 | { 331 | verbose( "." ); 332 | if (videoFrame == nil ) { 333 | verbose( "'nil' Frame captured.\n" ); 334 | return; 335 | } 336 | 337 | // Swap out old frame for new one 338 | CVImageBufferRef imageBufferToRelease; 339 | CVBufferRetain(videoFrame); 340 | 341 | @synchronized(self){ 342 | imageBufferToRelease = mCurrentImageBuffer; 343 | mCurrentImageBuffer = videoFrame; 344 | } // end sync 345 | CVBufferRelease(imageBufferToRelease); 346 | 347 | } 348 | 349 | @end 350 | 351 | // forward declaration 352 | int releaseCameras(lua_State *L); 353 | 354 | // static vars 355 | static int nbcams = 0; 356 | static ImageSnap **snap = NULL; 357 | static QTCaptureDevice **device = NULL; 358 | static NSAutoreleasePool * pool = NULL; 359 | 360 | // start up all cameras found 361 | int initCameras(lua_State *L) { 362 | // free cams 363 | if (nbcams > 0) { 364 | releaseCameras(L); 365 | } 366 | 367 | // pool 368 | if (pool == NULL) { 369 | pool = [[NSAutoreleasePool alloc] init]; 370 | } 371 | 372 | // get args 373 | nbcams = lua_objlen(L, 1); 374 | int width = lua_tonumber(L, 2); 375 | int height = lua_tonumber(L, 3); 376 | 377 | // find devices 378 | NSArray *deviceName = [ImageSnap videoDevices]; 379 | int k = 0; 380 | if ([deviceName count] > 0) { 381 | printf("found %ld video device(s):\n", [deviceName count]); 382 | for( QTCaptureDevice *name in deviceName ){ 383 | printf( "%d: %s\n", k++, [[name description] UTF8String] ); 384 | } 385 | } else { 386 | printf("no video devices found, aborting...\n"); 387 | return 0; 388 | } 389 | 390 | // init given cameras 391 | printf("user requested %d camera(s)\n", nbcams); 392 | if ([deviceName count] < nbcams) { 393 | nbcams = [deviceName count]; 394 | printf("only using the first %d camera(s)\n", nbcams); 395 | } 396 | device = malloc(sizeof(QTCaptureDevice *)*nbcams); 397 | int i = 0, j = 0; 398 | for( QTCaptureDevice *dev in deviceName ) { 399 | // next cam: 400 | for (int k=1; k<=nbcams; k++) { 401 | lua_rawgeti(L, 1, k); 402 | int user = lua_tonumber(L, -1); 403 | if (user == j) { 404 | device[i++] = [ImageSnap deviceNamed:[dev description]]; 405 | printf( "using device %d: %s\n", j, [[dev description] UTF8String] ); 406 | } 407 | lua_pop(L, 1); 408 | } 409 | j++; 410 | } 411 | 412 | // start snapshots 413 | snap = malloc(sizeof(ImageSnap *)*nbcams); 414 | for (int i=0; i 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | #define MAXIDX 100 33 | static CvCapture* capture[MAXIDX]; 34 | static IplImage* frame[MAXIDX]; 35 | static int fidx = 0; 36 | 37 | static int l_initCam(lua_State *L) { 38 | // args 39 | //int width = lua_tonumber(L, 2); 40 | //int height = lua_tonumber(L, 3); 41 | 42 | // max allocs ? 43 | if (fidx == MAXIDX) { 44 | perror("max nb of devices reached...\n"); 45 | } 46 | 47 | // if number, open a camera device 48 | if (lua_isnumber(L, 1)) { 49 | printf("initializing camera\n"); 50 | const int idx = lua_tonumber(L, 1); 51 | capture[fidx] = cvCaptureFromCAM(idx); 52 | if( capture[fidx] == NULL ) { 53 | perror("could not create OpenCV capture"); 54 | } 55 | // sleep(2); 56 | //cvSetCaptureProperty(capture[fidx], CV_CAP_PROP_FRAME_WIDTH, width); 57 | //cvSetCaptureProperty(capture[fidx], CV_CAP_PROP_FRAME_HEIGHT, height); 58 | frame[fidx] = cvQueryFrame ( capture[fidx] ); 59 | int tries = 10; 60 | //while ((!frame[fidx] || frame[fidx]->height != height || frame[fidx]->width != width) && tries>0) { 61 | //// The above while should only be used with cvSetCaptureProperty 62 | 63 | while ( !frame[fidx] && tries>0) { 64 | frame[fidx] = cvQueryFrame ( capture[fidx] ); 65 | tries--; 66 | sleep(1); 67 | } 68 | if ( frame[fidx] == NULL ) { 69 | perror("failed OpenCV test capture"); 70 | } 71 | if(frame[fidx]->depth != IPL_DEPTH_8U) { 72 | perror("initCam: opencv supported for 8-bit unsigned capture only"); 73 | } 74 | printf("camera initialized\n"); 75 | } else { 76 | // open a file; 77 | const char *file = lua_tostring(L, 1); 78 | printf("initializing frame grabber on file: %s\n", file); 79 | capture[fidx] = cvCreateFileCapture(file); 80 | if( capture[fidx] == NULL ) { 81 | perror("could not create OpenCV capture"); 82 | } 83 | } 84 | 85 | // next 86 | lua_pushnumber(L, fidx); 87 | fidx ++; 88 | return 1; 89 | } 90 | 91 | // frame grabber 92 | static int l_grabFrame (lua_State *L) { 93 | // Get Tensor's Info 94 | const int idx = lua_tonumber(L, 1); 95 | THFloatTensor * tensor = luaT_toudata(L, 2, "torch.FloatTensor"); 96 | 97 | // grab frame 98 | frame[idx] = cvQueryFrame ( capture[idx] ); 99 | if( !frame[idx] ) { 100 | perror("could not query OpenCV capture"); 101 | } 102 | 103 | // resize given tensor 104 | THFloatTensor_resize3d(tensor, 3, frame[idx]->height, frame[idx]->width); 105 | 106 | // copy to tensor 107 | int m0 = tensor->stride[1]; 108 | int m1 = tensor->stride[2]; 109 | int m2 = tensor->stride[0]; 110 | unsigned char *src = (unsigned char *) frame[idx]->imageData; 111 | float *dst = THFloatTensor_data(tensor); 112 | int i, j, k; 113 | for (i=0; i < frame[idx]->height; i++) { 114 | for (j=0, k=0; j < frame[idx]->width; j++, k+=m1) { 115 | // red: 116 | dst[k] = src[i*frame[idx]->widthStep + j*frame[idx]->nChannels + 2]/255.; 117 | // green: 118 | dst[k+m2] = src[i*frame[idx]->widthStep + j*frame[idx]->nChannels + 1]/255.; 119 | // blue: 120 | dst[k+2*m2] = src[i*frame[idx]->widthStep + j*frame[idx]->nChannels + 0]/255.; 121 | } 122 | dst += m0; 123 | } 124 | 125 | return 0; 126 | } 127 | 128 | static int l_releaseCam (lua_State *L) { 129 | const int idx = lua_tonumber(L, 1); 130 | cvReleaseCapture( &capture[idx] ); 131 | return 0; 132 | } 133 | 134 | // Register functions 135 | static const struct luaL_reg opencv [] = { 136 | {"initCam", l_initCam}, 137 | {"grabFrame", l_grabFrame}, 138 | {"releaseCam", l_releaseCam}, 139 | {NULL, NULL} /* sentinel */ 140 | }; 141 | 142 | int luaopen_libcamopencv (lua_State *L) { 143 | luaL_openlib(L, "libcamopencv", opencv, 0); 144 | return 1; 145 | } 146 | -------------------------------------------------------------------------------- /opencv/opencv.h: -------------------------------------------------------------------------------- 1 | static int l_initCam(lua_State*); 2 | static int l_grabFrame (lua_State*); 3 | static int l_releaseCam (lua_State*); 4 | int luaopen_libopencv (lua_State*); 5 | -------------------------------------------------------------------------------- /video4linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | SET(src v4l.c) 2 | SET(luasrc init.lua) 3 | 4 | SET(OMP 1) 5 | IF (OMP) 6 | FIND_PACKAGE(OpenMP) 7 | IF(OPENMP_FOUND) 8 | MESSAGE(STATUS "OpenMP Found with compiler flag : ${OpenMP_C_FLAGS}") 9 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") 10 | ENDIF(OPENMP_FOUND) 11 | ENDIF (OMP) 12 | 13 | FIND_PACKAGE(ARM) 14 | IF (NEON_FOUND) 15 | MESSAGE(STATUS "Neon found with compiler flag : -mfpu=neon -D__NEON__") 16 | SET(CMAKE_C_FLAGS "-mfpu=neon -D__NEON__ ${CMAKE_C_FLAGS}") 17 | ENDIF (NEON_FOUND) 18 | IF (CORTEXA8_FOUND) 19 | MESSAGE(STATUS "Cortex-A8 Found with compiler flag : -mcpu=cortex-a8") 20 | SET(CMAKE_C_FLAGS "-mcpu=cortex-a8 -fprefetch-loop-arrays ${CMAKE_C_FLAGS}") 21 | ENDIF (CORTEXA8_FOUND) 22 | IF (CORTEXA9_FOUND) 23 | MESSAGE(STATUS "Cortex-A9 Found with compiler flag : -mcpu=cortex-a9") 24 | SET(CMAKE_C_FLAGS "-mcpu=cortex-a9 ${CMAKE_C_FLAGS}") 25 | ENDIF (CORTEXA9_FOUND) 26 | 27 | ADD_TORCH_PACKAGE(v4l "${src}" "${luasrc}" "Image Processing") 28 | 29 | TARGET_LINK_LIBRARIES(v4l luaT TH) 30 | -------------------------------------------------------------------------------- /video4linux/init.lua: -------------------------------------------------------------------------------- 1 | 2 | ---------------------------------- 3 | -- dependencies 4 | ---------------------------------- 5 | require 'torch' 6 | require 'xlua' 7 | require 'image' 8 | require 'libv4l' 9 | 10 | ---------------------------------- 11 | -- a camera class 12 | ---------------------------------- 13 | local Camera = torch.class('image.Camera') 14 | 15 | function Camera:__init(...) 16 | -- parse args 17 | local args, idx, width, height, nbuffers, fps = xlua.unpack( 18 | {...}, 19 | 'ImageSource', help_desc, 20 | {arg='idx', type='number', help='camera index', default=0}, 21 | {arg='width', type='number', help='width', default=640}, 22 | {arg='height', type='number', help='height', default=480}, 23 | {arg='buffers', type='number', help='number of buffers (v4l2 only)', default=1}, 24 | {arg='fps', type='number', help='optional frame rate (v4l2 only)', default=1} 25 | ) 26 | -- init vars 27 | self.camidx = idx 28 | self.width = width 29 | self.height = height 30 | self.nbuffers = nbuffers 31 | self.fps = fps 32 | self.tensor = torch.FloatTensor(3,height,width) 33 | self.tensortyped = torch.Tensor(self.tensor:size()) 34 | libv4l.init(self.camidx, self.width, self.height, self.fps, self.nbuffers) 35 | end 36 | 37 | function Camera:adjustManualFocus(f) 38 | libv4l.adjustManualFocus(self.camidx,f) 39 | end 40 | 41 | function Camera:setManualFocus() 42 | libv4l.setFocusType(self.camidx,0) 43 | end 44 | 45 | function Camera:setAutoFocus() 46 | libv4l.setFocusType(self.camidx,1) 47 | end 48 | 49 | function Camera:setMacroFocus() 50 | libv4l.setFocusType(self.camidx,2) 51 | end 52 | 53 | function Camera:setContinuousFocus() 54 | libv4l.setFocusType(self.camidx,3) 55 | end 56 | 57 | function Camera:forward(tensor) 58 | libv4l.grabFrame(self.camidx, self.tensor) 59 | if tensor then 60 | if (self.tensor:type() ~= tensor:type()) then 61 | self.tensortyped:copy(self.tensor) 62 | image.scale(self.tensortyped, tensor) 63 | return tensor 64 | end 65 | image.scale(self.tensor, tensor) 66 | return tensor 67 | end 68 | if (self.tensor:type() ~= self.tensortyped:type()) then 69 | self.tensortyped:copy(self.tensor) 70 | return self.tensortyped 71 | end 72 | return self.tensor 73 | end 74 | 75 | function Camera:stop() 76 | print('stopping camera') 77 | end 78 | -------------------------------------------------------------------------------- /video4linux/v4l.c: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // File: v4l 3 | // 4 | // Description: A wrapper for a video4linux camera frame grabber 5 | // 6 | // Created: September 27, 2010, 10:20PM 7 | // 8 | // Author: Clement Farabet // clement.farabet@gmail.com 9 | // largely taken from Pierre Sermanet's EBLearn toolbox/ 10 | // [BSD licensed, http://eblearn.sourceforge.net/] 11 | //============================================================================== 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include /* for videodev2.h */ 31 | #include 32 | 33 | // video buffers 34 | #define MAX_BUFFERS 8 35 | #define MAX_CAMERAS 8 36 | 37 | typedef struct 38 | { 39 | int nbuffers; 40 | char device[80]; 41 | void *buffers[MAX_BUFFERS]; 42 | int sizes[MAX_BUFFERS]; 43 | int started; 44 | int fd; 45 | int width; 46 | int width1; 47 | int height; 48 | int height1; 49 | int fps; 50 | } Cam; 51 | 52 | static Cam Cameras[MAX_CAMERAS]; 53 | static int camidx = 0; 54 | 55 | 56 | static int xioctl(int fd, int request, void *arg) 57 | { 58 | int r; 59 | 60 | do r = ioctl (fd, request, arg); 61 | while (-1 == r && EINTR == errno); 62 | 63 | return r; 64 | } 65 | 66 | 67 | // camera control 68 | static void set_boolean_control(Cam *camera, int id, int val) { 69 | struct v4l2_control control; 70 | 71 | memset (&control, 0, sizeof (control)); 72 | control.id = id; 73 | if (0 == ioctl (camera->fd, VIDIOC_G_CTRL, &control)) { 74 | control.value += 1; 75 | /* The driver may clamp the value or return ERANGE, ignored here */ 76 | if (-1 == ioctl (camera->fd, VIDIOC_S_CTRL, &control) 77 | && errno != ERANGE) { 78 | perror("VIDIOC_S_CTRL"); 79 | } 80 | /* Ignore if V4L2_CID_CONTRAST is unsupported */ 81 | } else if (errno != EINVAL) { 82 | perror("VIDIOC_G_CTRL"); 83 | exit (EXIT_FAILURE); 84 | } 85 | control.id = id; 86 | control.value = val; 87 | /* Errors ignored */ 88 | ioctl (camera->fd, VIDIOC_S_CTRL, &control); 89 | } 90 | 91 | 92 | static int l_set_focus_type(lua_State *L){ 93 | // device 94 | Cam *camera; 95 | int camid = 0; 96 | int val = 0; 97 | if (lua_isnumber(L, 1)) camid = lua_tonumber(L, 1); 98 | camera = &Cameras[camid]; 99 | if (camera->started != 1){ 100 | printf("Camera not open at this index\n"); 101 | return -1; 102 | } 103 | if (lua_isnumber(L, 2)) val = lua_tonumber(L,2); 104 | /* val can be : 105 | * enum v4l2_focus_auto_type { 106 | * V4L2_FOCUS_MANUAL = 0, 107 | * V4L2_FOCUS_AUTO = 1, 108 | * V4L2_FOCUS_MACRO = 2, 109 | * V4L2_FOCUS_CONTINUOUS = 3 110 | * } 111 | */ 112 | if ((val >= 0) && (val <= 4)){ 113 | set_boolean_control(camera,V4L2_CID_FOCUS_AUTO, val); 114 | } else { 115 | printf("Bad value for setFocusType\n"); 116 | } 117 | return 0; 118 | } 119 | 120 | static int l_adjust_manual_focus(lua_State *L){ 121 | // device 122 | Cam *camera; 123 | int camid = 0; 124 | int val = 0; 125 | if (lua_isnumber(L, 1)) camid = lua_tonumber(L, 1); 126 | camera = &Cameras[camid]; 127 | if (camera->started != 1){ 128 | printf("Camera not open at this index\n"); 129 | return -1; 130 | } 131 | if (lua_isnumber(L, 2)) val = lua_tonumber(L,2); 132 | /* set to manual focus */ 133 | set_boolean_control(camera,V4L2_CID_FOCUS_AUTO, 0); 134 | set_boolean_control(camera,V4L2_CID_FOCUS_ABSOLUTE, val); 135 | return 0; 136 | } 137 | 138 | 139 | static int open_device(int camid, char *dev_name) 140 | { 141 | Cam * camera = &Cameras[camid]; 142 | struct stat st; 143 | 144 | if (-1 == stat (dev_name, &st)) { 145 | fprintf (stderr, "Cannot identify '%s': %d, %s\n", 146 | dev_name, errno, strerror (errno)); 147 | return -1; 148 | } 149 | 150 | if (!S_ISCHR (st.st_mode)) { 151 | fprintf (stderr, "%s is no device\n", dev_name); 152 | return -1; 153 | } 154 | 155 | camera->fd = open(dev_name, O_RDWR); 156 | 157 | if (-1 == camera->fd) { 158 | fprintf (stderr, "Cannot open '%s': %d, %s\n", 159 | dev_name, errno, strerror (errno)); 160 | return -1; 161 | } 162 | 163 | return 0; 164 | } 165 | 166 | 167 | static int init_capability(int camid) 168 | { 169 | Cam * camera = &Cameras[camid]; 170 | 171 | struct v4l2_capability cap; 172 | memset((void*) &cap, 0, sizeof(struct v4l2_capability)); 173 | 174 | if (-1 == xioctl (camera->fd, VIDIOC_QUERYCAP, &cap)) { 175 | if (EINVAL == errno) { 176 | perror("no V4L2 device found"); 177 | return -1; 178 | } else { 179 | return -1; 180 | } 181 | } 182 | 183 | if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { 184 | perror("v4l2 device does not support video capture"); 185 | return -1; 186 | } 187 | 188 | if (!(cap.capabilities & V4L2_CAP_STREAMING)) { 189 | perror("v4l2 device does not support streaming i/o"); 190 | return -1; 191 | } 192 | 193 | return 0; 194 | } 195 | 196 | 197 | static int init_format(int camid, int width, int height) 198 | { 199 | Cam * camera = &Cameras[camid]; 200 | 201 | // resetting cropping to full frame 202 | struct v4l2_cropcap cropcap; 203 | struct v4l2_crop crop; 204 | memset((void*) &cropcap, 0, sizeof(struct v4l2_cropcap)); 205 | 206 | cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 207 | if (0 == ioctl(camera->fd, VIDIOC_CROPCAP, &cropcap)) { 208 | crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 209 | crop.c = cropcap.defrect; 210 | ioctl(camera->fd, VIDIOC_S_CROP, &crop); 211 | } 212 | 213 | // set format 214 | struct v4l2_format fmt; 215 | 216 | memset((void*) &fmt, 0, sizeof(struct v4l2_format)); 217 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 218 | 219 | #if defined __NEON__ 220 | // Check that line width is multiple of 16 221 | if(0 != (width % 16)) 222 | { 223 | width = width & 0xFFFFFFF0; 224 | } 225 | #endif 226 | 227 | // TODO: error when ratio not correct 228 | fmt.fmt.pix.width = width; 229 | fmt.fmt.pix.height = height; 230 | fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; 231 | fmt.fmt.pix.field = V4L2_FIELD_ANY; 232 | if (ioctl(camera->fd, VIDIOC_S_FMT, &fmt) < 0) { 233 | perror("unable to set v4l2 format"); 234 | return -1; 235 | } 236 | camera->height = height; 237 | camera->width = width; 238 | camera->height1 = fmt.fmt.pix.height; 239 | camera->width1 = fmt.fmt.pix.width; 240 | 241 | if (camera->height != camera->height1 || camera->width != camera->width1) { 242 | printf("Warning: camera resolution changed to %dx%d\n", 243 | camera->height1, camera->width1); 244 | } 245 | 246 | return 0; 247 | } 248 | 249 | 250 | static int init_controls(int camid, int fps) 251 | { 252 | Cam * camera = &Cameras[camid]; 253 | 254 | // set framerate 255 | camera->fps = fps; 256 | 257 | struct v4l2_streamparm setfps; 258 | memset((void*) &setfps, 0, sizeof(struct v4l2_streamparm)); 259 | setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 260 | setfps.parm.capture.timeperframe.numerator = 1; 261 | setfps.parm.capture.timeperframe.denominator = camera->fps; 262 | ioctl(camera->fd, VIDIOC_S_PARM, &setfps); 263 | 264 | // set color controls 265 | set_boolean_control(camera,V4L2_CID_AUTOGAIN, 1); 266 | set_boolean_control(camera,V4L2_CID_AUTO_WHITE_BALANCE, 1); 267 | 268 | return 0; 269 | } 270 | 271 | 272 | static int init_buffers(int camid, int nbuffers) 273 | { 274 | Cam * camera = &Cameras[camid]; 275 | 276 | // allocate and map the buffers 277 | camera->nbuffers = nbuffers; 278 | 279 | struct v4l2_requestbuffers rb; 280 | rb.count = camera->nbuffers; 281 | rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 282 | rb.memory = V4L2_MEMORY_MMAP; 283 | int ret = ioctl(camera->fd, VIDIOC_REQBUFS, &rb); 284 | if (ret < 0) { 285 | perror("could not allocate v4l2 buffers"); 286 | return -1; 287 | } 288 | ret = 0; 289 | int i; 290 | for (i = 0; i < camera->nbuffers; i++) { 291 | struct v4l2_buffer buf; 292 | int r; 293 | memset((void*) &buf, 0, sizeof(struct v4l2_buffer)); 294 | buf.index = i; 295 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 296 | buf.memory = V4L2_MEMORY_MMAP; 297 | r = ioctl(camera->fd, VIDIOC_QUERYBUF, &buf); 298 | if (r < 0) 299 | ret = -(i+1); 300 | if (ret == 0) { 301 | camera->buffers[i] = 302 | mmap(0, buf.length, PROT_READ + PROT_WRITE, MAP_SHARED, 303 | camera->fd, buf.m.offset); 304 | camera->sizes[i] = buf.length; 305 | if (camera->buffers[i] == MAP_FAILED) 306 | ret = -(i+1000); 307 | } 308 | } 309 | 310 | if (ret < 0) { 311 | printf("ret = %d\n", ret); 312 | if (ret > -1000) { 313 | printf("query buffer %d\n", - (1 + ret)); 314 | perror("could not query v4l2 buffer"); 315 | return -1; 316 | } else { 317 | printf("map buffer %d\n", - (1000 + ret)); 318 | perror("could not map v4l2 buffer"); 319 | return -1; 320 | } 321 | } 322 | 323 | return 0; 324 | } 325 | 326 | 327 | static int init_camera(int camid, int width, int height, int fps, int nbuffers) 328 | { 329 | if (0 > init_capability(camid)) { 330 | return -1; 331 | } 332 | 333 | if (0 > init_format(camid, width, height)) { 334 | return -1; 335 | } 336 | 337 | if (0 > init_controls(camid, fps)) { 338 | return -1; 339 | } 340 | 341 | if (0 > init_buffers(camid, nbuffers)) { 342 | return -1; 343 | } 344 | 345 | return 0; 346 | } 347 | 348 | 349 | static int start_capturing(int camid) 350 | { 351 | Cam * camera = &Cameras[camid]; 352 | 353 | int ret = 0; 354 | int i; 355 | for (i = 0; i < camera->nbuffers; ++i) { 356 | struct v4l2_buffer buf; 357 | memset((void*) &buf, 0, sizeof(struct v4l2_buffer)); 358 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 359 | buf.memory = V4L2_MEMORY_MMAP; 360 | buf.index = i; 361 | ret += ioctl(camera->fd, VIDIOC_QBUF, &buf); 362 | 363 | if (ret < 0) { 364 | printf("WARNING: could not enqueue camera %d, v4l2 buffer %d: errno %d\n", 365 | camid, i, ret); 366 | } 367 | } 368 | 369 | enum v4l2_buf_type type; 370 | type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 371 | ret = ioctl(camera->fd, VIDIOC_STREAMON, &type); 372 | 373 | if (ret < 0) { 374 | fprintf(stderr, "could not start v4l2 capture: errno %d\n", ret); 375 | camera->started = 0; 376 | return -1; 377 | } 378 | 379 | camera->started = 1; 380 | printf("camera[%d] started : %d\n",camid,camera->started); 381 | 382 | return 0; 383 | } 384 | 385 | 386 | // frame grabber 387 | static int l_init (lua_State *L) { 388 | Cam * camera = NULL; 389 | 390 | int camid = 0; 391 | int width = 640; 392 | int height = 480; 393 | int fps = 1; 394 | int nbuffers = 1; 395 | 396 | // camera device id 397 | if (lua_isnumber(L, 1)) camid = lua_tonumber(L, 1); 398 | camera = &Cameras[camid]; 399 | sprintf(camera->device,"/dev/video%d",camid); 400 | printf("Initializing device: %s\n", camera->device); 401 | 402 | // width at which to grab 403 | if (lua_isnumber(L, 2)) width = lua_tonumber(L, 2); 404 | 405 | // height at which to grab 406 | if (lua_isnumber(L, 3)) height = lua_tonumber(L, 3); 407 | 408 | // driver frame rate 409 | if (lua_isnumber(L, 4)) fps = lua_tonumber(L, 4); 410 | printf("FPS wanted %d \n", fps); 411 | 412 | // nb of driver buffers 413 | if (lua_isnumber(L, 5)) nbuffers = lua_tonumber(L, 5); 414 | printf("Using %d buffers\n", nbuffers); 415 | 416 | // open 417 | if (0 > open_device(camid, camera->device)) { 418 | lua_pushboolean(L, 0); 419 | return 1; 420 | } 421 | 422 | // init 423 | if (0 > init_camera(camid, width, height, fps, nbuffers)) { 424 | lua_pushboolean(L, 0); 425 | return 1; 426 | } 427 | 428 | // start 429 | if (0 > start_capturing(camid)) { 430 | lua_pushboolean(L, 0); 431 | return 1; 432 | } 433 | 434 | lua_pushboolean(L, 1); 435 | return 1; 436 | } 437 | 438 | 439 | static void copy_frame_rgb(int camid, float * dst, int stride_width, int stride_data, int stride_image) 440 | { 441 | Cam * camera = &Cameras[camid]; 442 | 443 | unsigned char *src, *srcp; 444 | float *dstp; 445 | int i, j, j2, k; 446 | int ret = 0; 447 | 448 | struct v4l2_buffer buf; 449 | memset((void*) &buf, 0, sizeof(struct v4l2_buffer)); 450 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 451 | buf.memory = V4L2_MEMORY_MMAP; 452 | ret = ioctl(camera->fd, VIDIOC_DQBUF, &buf); 453 | src = (unsigned char *)(camera->buffers[buf.index]); 454 | 455 | int width1 = camera->width1; 456 | int height1 = camera->height1; 457 | 458 | float fl_cst_val[5] = {0.00456620784314, 459 | 0.00625892941176, 460 | 0.00318810980392, 461 | 0.00791071372549, 462 | 0.00153618039216}; 463 | float f,y,u,v; 464 | 465 | #pragma omp parallel for shared(src, dst, fl_cst_val, width1, camera) private(i,j,j2,k,srcp,dstp,y,u,v) 466 | for (i=0; i < camera->height1; i++) { 467 | srcp = &src[i * (camera->width1 << 1)]; 468 | dstp = &dst[i * stride_width]; 469 | 470 | // Replace the grab line part by an Neon optimized version 471 | #if defined __NEON__ 472 | // This assembly part operate on one line of the frame 473 | __asm__ __volatile__ ( 474 | "mov r0, %1 @ Make a working float const pointer \n\t" 475 | "mov r2, %0 @ Make a working src pointer \n\t" 476 | "mov r1, #0 @ init width1 counter \n\t" 477 | "mov r3, #128 @ Use temp register r3 to store int const \n\t" 478 | "mov r4, #16 @ Use temp register r4 to store int const\n\t" 479 | "vld4.8 {d16,d17,d18,d19},[r2]! @ \n\t" 480 | "vdup.8 d2, r3 @ \n\t" 481 | "vdup.8 d3, r4 @ \n\t" 482 | "mov r3, %2 @ pointer on dst R \n\t" 483 | "lsls r5, %3, #2 @ Multiply stride by size of float \n\t" 484 | "adds r4, r3, r5 @ Add stride image for 2nd component \n\t" 485 | "adds r5, r4, r5 @ Add stride image again for 3th component \n\t" 486 | "1: @ loop on line \n\t" 487 | "vsubl.u8 q2, d16, d3 @ \n\t" 488 | "vsubl.u8 q3, d17, d2 @ \n\t" 489 | "vld1.32 {d0,d1}, [r0]! @ \n\t" 490 | "vsubl.u8 q5, d19, d2 @ \n\t" 491 | "vsubl.u8 q4, d18, d3 @ \n\t" 492 | "vmovl.s16 q6, d4 @ \n\t" 493 | "vmovl.s16 q7, d6 @ \n\t" 494 | "vmovl.s16 q8, d8 @ \n\t" 495 | "vmovl.s16 q9, d10 @ \n\t" 496 | "vcvt.f32.s32 q6, q6 @ \n\t" 497 | "vcvt.f32.s32 q7, q7 @ \n\t" 498 | "vcvt.f32.s32 q8, q8 @ \n\t" 499 | "vcvt.f32.s32 q9, q9 @ \n\t" 500 | "vmul.f32 q10, q6, d0[0] @ \n\t" 501 | "vmul.f32 q12, q6, d0[0] @ \n\t" 502 | "vmul.f32 q14, q6, d0[0] @ \n\t" 503 | "vmul.f32 q11, q8, d0[0] @ \n\t" 504 | "vmul.f32 q13, q8, d0[0] @ \n\t" 505 | "vmul.f32 q15, q8, d0[0] @ \n\t" 506 | "vld1.32 d0[0], [r0] @ \n\t" 507 | "vmls.f32 q12, q9, d1[0] @ \n\t" 508 | "vmla.f32 q10, q9, d0[1] @ \n\t" 509 | "vmla.f32 q14, q7, d1[1] @ \n\t" 510 | "mov r0, %1 @ \n\t" 511 | "vmls.f32 q13, q9, d1[0] @ \n\t" 512 | "vmla.f32 q11, q9, d0[1] @ \n\t" 513 | "vmla.f32 q15, q7, d1[1] @ \n\t" 514 | "vmls.f32 q12, q7, d0[0] @ \n\t" 515 | "vmls.f32 q13, q7, d0[0] @ \n\t" 516 | "vld1.32 {d0,d1}, [r0]! @ \n\t" 517 | "vzip.32 q10, q11 @ \n\t" 518 | "vzip.32 q14, q15 @ \n\t" 519 | "vzip.32 q12, q13 @ \n\t" 520 | "vst1.32 {d20-d23}, [r3]! @ \n\t" 521 | "vst1.32 {d24-d27}, [r4]! @ \n\t" 522 | "vst1.32 {d28-d31}, [r5]! @ \n\t" 523 | "vmovl.s16 q6, d5 @ \n\t" 524 | "vmovl.s16 q7, d7 @ \n\t" 525 | "vmovl.s16 q8, d9 @ \n\t" 526 | "vmovl.s16 q9, d11 @ \n\t" 527 | "vcvt.f32.s32 q6, q6 @ \n\t" 528 | "vcvt.f32.s32 q7, q7 @ \n\t" 529 | "vcvt.f32.s32 q8, q8 @ \n\t" 530 | "vcvt.f32.s32 q9, q9 @ \n\t" 531 | "vmul.f32 q10, q6, d0[0] @ \n\t" 532 | "vmul.f32 q12, q6, d0[0] @ \n\t" 533 | "vmul.f32 q14, q6, d0[0] @ \n\t" 534 | "vmul.f32 q11, q8, d0[0] @ \n\t" 535 | "vmul.f32 q13, q8, d0[0] @ \n\t" 536 | "vmul.f32 q15, q8, d0[0] @ \n\t" 537 | "vld1.32 d0[0], [r0] @ \n\t" 538 | "vmls.f32 q12, q9, d1[0] @ \n\t" 539 | "vmla.f32 q10, q9, d0[1] @ \n\t" 540 | "mov r0, %1 @ \n\t" 541 | "vmla.f32 q14, q7, d1[1] @ \n\t" 542 | "vmls.f32 q13, q9, d1[0] @ \n\t" 543 | "vmla.f32 q11, q9, d0[1] @ \n\t" 544 | "vmla.f32 q15, q7, d1[1] @ \n\t" 545 | "vmls.f32 q12, q7, d0[0] @ \n\t" 546 | "vmls.f32 q13, q7, d0[0] @ \n\t" 547 | "adds r1, r1, #16 @ Increment widthl counter\n\t" 548 | "cmp r1, %4 @ Check end of line\n\t" 549 | "bge 3f @ \n\t" 550 | "2: @ \n\t" 551 | "vld4.8 {d16,d17,d18,d19},[r2]! @ Load next 16 pixels \n\t" 552 | "vzip.32 q10, q11 @ \n\t" 553 | "vzip.32 q14, q15 @ \n\t" 554 | "vzip.32 q12, q13 @ \n\t" 555 | "vst1.32 {d20-d23}, [r3]! @ \n\t" 556 | "vst1.32 {d24-d27}, [r4]! @ \n\t" 557 | "vst1.32 {d28-d31}, [r5]! @ \n\t" 558 | "b 1b @ \n\t" 559 | "3: @ \n\t" 560 | "vzip.32 q10, q11 @ \n\t" 561 | "vzip.32 q14, q15 @ \n\t" 562 | "vzip.32 q12, q13 @ \n\t" 563 | "vst1.32 {d20-d23}, [r3]! @ \n\t" 564 | "vst1.32 {d24-d27}, [r4]! @ \n\t" 565 | "vst1.32 {d28-d31}, [r5]! @ \n\t" 566 | : 567 | :"r" (srcp),"r" (fl_cst_val),"r"(dstp),"r"(stride_image),"r"(width1) 568 | : "cc", "r0", "r1", "r2", "r3", "r4", "r5", "memory", 569 | "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", 570 | "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15", 571 | "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", 572 | "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", 573 | "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", 574 | "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31" 575 | ); 576 | #else 577 | for (j=0, k=0; j < camera->width1; j++, k+=stride_data) { 578 | 579 | j2 = j<<1; 580 | 581 | // get Y,U,V chanels from bayer pattern 582 | y = (float)srcp[j2]; 583 | if (j & 1) { 584 | u = (float)srcp[j2-1]; v = (float)srcp[j2+1]; 585 | } else { 586 | u = (float)srcp[j2+1]; v = (float)srcp[j2+3]; 587 | } 588 | 589 | // convert to RGB 590 | // red: 591 | dstp[k] = 0.00456*(y-16) + 0.00625*(v-128); 592 | // green: 593 | dstp[k+stride_image] = 0.00456*(y-16) - 0.00318*(v-128) - 0.001536*(u-128); 594 | // blue: 595 | dstp[k+2*stride_image] = 0.00456*(y-16) + 0.007910*(u-128); 596 | } 597 | #endif 598 | } 599 | ret += ioctl(camera->fd, VIDIOC_QBUF, &buf); 600 | } 601 | 602 | 603 | // frame grabber 604 | static int l_grabFrame (lua_State *L) 605 | { 606 | // device 607 | Cam *camera; 608 | int camid = 0; 609 | if (lua_isnumber(L, 1)) camid = lua_tonumber(L, 1); 610 | camera = &Cameras[camid]; 611 | if (camera->started != 1){ 612 | perror("Camera not open at this index"); 613 | lua_pushboolean(L, 0); 614 | return 1; 615 | } 616 | // refrence tensor 617 | THFloatTensor * frame = 618 | luaT_toudata(L, 2, luaT_typenameid(L, "torch.FloatTensor")); 619 | 620 | // resize given tensor 621 | THFloatTensor_resize3d(frame, 3, 622 | camera->height1, camera->width1); 623 | 624 | // get tensor's Info 625 | int stride_image = frame->stride[0]; 626 | int stride_width = frame->stride[1]; 627 | int stride_data = frame->stride[2]; 628 | 629 | float *dst = THFloatTensor_data(frame); 630 | 631 | copy_frame_rgb(camid, dst, stride_width, stride_data, stride_image); 632 | 633 | lua_pushboolean(L, 1); 634 | return 1; 635 | } 636 | 637 | 638 | static int l_releaseCam (lua_State *L) { 639 | return 0; 640 | } 641 | 642 | 643 | // Register functions in LUA 644 | static const struct luaL_reg v4l [] = { 645 | {"init" , l_init}, 646 | {"grabFrame" , l_grabFrame}, 647 | {"releaseCam" , l_releaseCam}, 648 | {"setFocusType" , l_set_focus_type}, 649 | {"adjustManualFocus", l_adjust_manual_focus}, 650 | {NULL, NULL} /* sentinel */ 651 | }; 652 | 653 | int luaopen_libv4l (lua_State *L) { 654 | luaL_openlib(L, "libv4l", v4l, 0); 655 | return 1; 656 | } 657 | --------------------------------------------------------------------------------