As explained in a previous post, Red is able to access usb cameras under macOS and Windows. The idea is now to record the content of the camera and then to read back the recorded movie file. To do that I implemented a specific format for Red Camera which allows to record and to read red video files.
RCAM (RedCAMera) Format
RCAM files contain rgb values.
RCAM files are stored as binary values.
RCAM files
contain a 32-byte header starting at offset 0 within the binary file. Video
data are beginning just after the header at offset 32.
RgbSize (Image x size *
Image y size * 3) is used to calculate the offset of each image contained in
the file.
Offset
|
Size
|
Description
|
0
|
4
|
RCAM Four
CC Red signature
|
4
|
4
|
Number of
images in the file
|
8
|
4
|
Image x
size
|
12
|
4
|
Image y size
|
16
|
8
|
duration
in sec (float value)
|
24
|
4
|
Frames by
Sec
|
28
|
4
|
compressed
data (1) or not (0)
|
32
|
Image x
size * Image y size * 3
|
Binary
values for all images
|
Each image
is then stored in a block! which is very convenient for a fast access and for
all operations that can be performed on blocks with Red.
Storing video data
Basically we need a block that will be used to store the rgb values provided by the camera such asmovie: copy []
Then for each sampled image it is really simple to store the values into te block:
img: to-image cam
append movie img/rgb
Lastly a simple write/binary is used to store the data into a binary file (see saveData function for details)
Code sample
Red [
Title: "Test camera Red VID "
Author: "Francois Jouen"
File: %reccam.red
Needs: View
]
iSize: 320x240
margins: 10x10
cam: none ; for camera
imgSize: 0
count: 0
cti: 0%
movie: copy []
t1: t2: now/time/precise
fn: %video.rvf
d: 0.0
fps: 0
compression: 0
processCam: does [
count: count + 1
ct/text: form count
if cb/data [
img: to-image cam
append movie img/rgb
]
]
saveData: does [
n: length? movie
i: 1
write/binary fn "RCAM" ;Four CC Red signature
write/binary/append fn to-binary n ;Number of images
write/binary/append fn to-binary img/size/x ;Image x size
write/binary/append fn to-binary img/size/y ;Image y size
write/binary/append fn to-binary d ;duration in sec
write/binary/append fn to-binary fps ;FPS
write/binary/append fn to-binary compression ;compressed data (1) or not (0)
foreach im movie [
cti: to-percent i / to-float n
write/binary/append fn movie/:i ;binary values
p1/data: cti
i: i + 1
]
]
view win: layout [
title "Red Cam"
origin margins space margins
cSize: field 100
cb: check "Record Camera" true
pad 25x0
btnQuit: button "Quit" 60x24 on-click [quit]
return
ct: field 100
button "Save" [saveData]
p1: progress 130
return
cam: camera iSize
return
active: text 55 "Camera" rate 0:0:1 on-time [processCam]
cam-list: drop-list 160 on-create [
face/data: cam/data
]
onoff: button "Start/Stop" on-click [
either cam/selected [
t2: now/time/precise
d: to-float t2 - t1
fps: to-integer round count / d
cam/selected: none
active/rate: none
][
cam/selected: cam-list/selected
active/rate: 0:0:0.04; max 1/25 fps in ms
img: to-image cam
cSize/text: form img/size
imgSize: (img/size/x * img/size/y) * 3
movie: copy []
count: 0
t1: now/time/precise
]
]
do [cam-list/selected: 1 active/rate: none]
]
Reading video files
The reading of red video files is also very simple. You just need to load the file as a binary file and get the 32-bytes header which contains all information required for a correct reading such as the number of images, the size of images or the duration of the movie. With x and y size of the image it's easy to compute the offset of image. Since we use rgb value, the total size of each image equals to x * y * 3. With this value (rgbSize) a simple skip on data allows to get rgb values for each image:
while [i < nImages] [
if i > 0 [f: skip f rgbSize]
rgb: copy/part f rgbSize
append movie rgb
i: i + 1
]
]
Code sample
Red [
Title: "Test camera Red VID "
Author: "Francois Jouen"
File: %movie.red
Needs: View redCV
]
margins: 5x5
fn: %video.rvf
iSize: 640x480
imgSize: 0x0
cSize: 0x0
n: 0
nImages: 0
strSize: 0
f: none
isFile: false
rgbSize: 0
img: make image! reduce [iSize black]
activeImage: 1
duration: 0.0
fps: 0
compression: 0
freq: none
readImage: func [n [integer!]][
if isFile [
f5/text: form n
img/rgb: movie/:n
]
canvas/image: img
]
readAllImages: does [
either activeImage < nImages [activeImage: activeImage + 1 readImage activeImage]
[activeImage: 1]
]
loadMovie: func [] [
;tmp: request-file/filter ["Red Video Files" "*.rvf"] ; pb with -t macOS
tmp: request-file
if not none? tmp [
f: read/binary tmp
; read header
s: to-string copy/part f 4 ; should be "RCAM"
f: skip f 4 ;
nImages: to-integer copy/part f 4
f: skip f 4
imgSize/x: to-integer copy/part f 4
f: skip f 4
imgSize/y: to-integer copy/part f 4
rgbSize: (imgSize/x * imgSize/y) * 3
f: skip f 4
duration: to-float copy/part f 8
f: skip f 8
fps: to-integer copy/part f 4
f: skip f 4
compression: to-integer copy/part f 4
either compression = 0 [f6/text: rejoin [ form compression " : Uncompressed video"]]
[f6/text: rejoin [ form compression " : ZLib compressed video"]]
f1/text: rejoin [form nImages " images"]
f2/text: form imgSize
f3/text: rejoin [form round duration " sec"]
f4/text: rejoin [form fps " FPS"]
freq: to-time compose [0 0 (1.0 / fps)]
f: skip f 4
; read red movie data
movie: copy []
i: 0
while [i < nImages] [
if i > 0 [f: skip f rgbSize]
rgb: copy/part f rgbSize
append movie rgb
i: i + 1
]
img: make image! reduce [imgSize rgb]
isFile: true
activeImage: 1
readImage activeImage
]
]
view win: layout [
title "Reading red movie"
origin margins space margins
button "Load" [loadMovie]
f1: field 100
f2: field 100
f3: field 100
f4: field 100
bt: base 20x20 on-time [readAllImages]
button "Quit" [quit]
return
canvas: base iSize img
return
button "<<" [activeImage: 1 readImage activeImage]
button ">>" [activeImage: nImages readImage activeImage]
button ">" [if activeImage < nImages [activeImage: activeImage + 1 readImage activeImage]]
button "<" [if activeImage > 1 [activeImage: activeImage - 1 readImage activeImage]]
button "<>" [if isFile [bt/rate: freq]]
button "||" [bt/rate: none]
f5: field 60
f6: field 160
do [bt/rate: none]
]
Very cool, Francois!
RépondreSupprimerThanks Bo, You're welcome :)
SupprimerSo you have a hard image limit of 32bit? Do you ever fore see needing more than that? High speed videos on a camera that is monitoring tree growth over several years, or something?
RépondreSupprimerThanks William
RépondreSupprimerThis code is still experimental and under progress :). I'm actually try to compress image data for a better rendering and downsizing video files.
See new version in Read movies with Red new post
RépondreSupprimer