In the previous post, we explained how Red can write compressed webcam images to a file. Of course, Red is also able to read back stored images in file. This is possible with 2 functions: readHeader and readImage.
readHeader Function
As explain in a previous post, Red Video Files (*.rvf) include a 36-byte header which contains all information we need to read video cam. Video file is used as parameter for this first function which reads as binary the first 36 bytes of the file and gets all the information about the movie such as the number of images, the image size or fps and so on.
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
|
4
|
DATA
|
readHeader: func [file [file!]][
f: read/binary/part file headerSize ; 36 bytes for the header
s: to-string copy/part f 4 ; should be "RCAM"
nImages: to-integer copy/part skip f 4 4 ; number of images in movie
imgSize/x: to-integer copy/part skip f 8 4 ; image X size
imgSize/y: to-integer copy/part skip f 12 4 ; image Y size
duration: to-float copy/part skip f 16 8 ; movie duration in sec
fps: to-integer copy/part skip f 24 4 ; frames/sec
zComp: to-integer copy/part skip f 28 4 ; compressed or uncompressed data
s: to-string copy/part skip f 32 4 ; should be "DATA"
]
After reading the file header, the second operation (see loadMovie function in code sample) is to get the offset of each image included in the movie. We use a simple block! datatype (movie) to store the list of images offset as explained:
movie: copy []
i: 0
; makes image offset index
nextIndex: headerSize ; 36 bytes
while [i < nImages] [
index: nextindex
rgbSize: to-integer copy/part f 4
nextindex: index + rgbSize + 8
f: skip f rgbSize + 8
append movie index
i: i + 1
]
readImage Function
Then it is really simple to write a function which takes the image number as parameter and gets image data associated to the image offset value. Since images are compressed or uncompressed we need to get both sizes (rgbSize and urgbSize). Each image
is organized as
follows:
rgbSize
|
urgbSize
|
rgb
|
Compressed
Image Size
|
Non-compressed
Image Size
|
binary rgb
values
|
4 bytes
|
4 bytes
|
rgbSize
bytes
|
readImage: func [n [integer!]][
idx: movie/:n ; get image offset
rgbSize: to-integer copy/part skip f idx 4 ; get compressed size
urgbSize: to-integer copy/part skip f idx + 4 4 ; get uncompressed size
rgb: copy/part skip f idx + 8 rgbSize ; get binary values
;decompress if necessary
either zComp = 0 [img/rgb: rgb] [img/rgb: rcvDecompressRGB rgb urgbSize]
canvas/image: img ; update image container
]
Code sample
Red [
Title: "Test camera Red VID "
Author: "Francois Jouen"
File: %movie.red
Needs: View redCV
]
#include %../../libs/redcv.red ; for redCV functions
margins: 5x5
iSize: 640x480
imgSize: 0x0
nImages: rgbSize: urgbSize: 0
img: rcvCreateImage iSize
currentImg: 1
duration: 0.0
fps: 0
freq: to-time compose [0 0 0.0]
zComp: 0
headerSize: 36
f: none
isFile: false
readImage: func [n [integer!]][
if isFile[
f5/text: form n
idx: movie/:n ; get image offset
rgbSize: to-integer copy/part skip f idx 4 ; get compressed size
urgbSize: to-integer copy/part skip f idx + 4 4 ; get uncompressed size
rgb: copy/part skip f idx + 8 rgbSize ; get binary values
;decompress if necessary
either zComp = 0 [img/rgb: rgb] [img/rgb: rcvDecompressRGB rgb urgbSize]
canvas/image: img ; update image container
]
]
updateSlider: does [sl/data: to-percent (currentImg / to-float nImages)]
readAllImages: does [
either currentImg < nImages [currentImg: currentImg + 1 readImage currentImg]
[currentImg: 0]
updateSlider
]
readHeader: func [file [file!]][
f: read/binary/part file headerSize ; 36 bytes for the header
s: to-string copy/part f 4 ; should be "RCAM"
nImages: to-integer copy/part skip f 4 4 ; number of images in movie
imgSize/x: to-integer copy/part skip f 8 4 ; image X size
imgSize/y: to-integer copy/part skip f 12 4 ; image Y size
duration: to-float copy/part skip f 16 8 ; movie duration in sec
fps: to-integer copy/part skip f 24 4 ; frames/sec
zComp: to-integer copy/part skip f 28 4 ; compressed or uncompressed data
s: to-string copy/part skip f 32 4 ; should be "DATA"
; update fields and variables
either zComp = 0 [f6/text: rejoin [ form zComp " : Uncompressed video"]]
[f6/text: rejoin [ form zComp " : ZLib compressed video"]]
f1/text: rejoin [form nImages " frames"]
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)]
]
loadMovie: func [] [
tmp: request-file/filter ["Red Video Files" "*.rvf"]
if not none? tmp [
readHeader tmp ; read movie header
f: read/binary/seek tmp headerSize ; go to first image
movie: copy []
i: 0
; makes image offset index
nextIndex: headerSize ; 36 bytes
while [i < nImages] [
index: nextindex
rgbSize: to-integer copy/part f 4
nextindex: index + rgbSize + 8
f: skip f rgbSize + 8
append movie index
i: i + 1
]
isFile: true
sl/data: 0%
f: read/binary tmp ; head of file
img: rcvCreateImage imgSize ; we need a red image! for displaying video
currentImg: 1
readImage currentImg
win/text: copy form tmp
]
]
view win: layout [
title "Reading red movie"
origin margins space margins
button "Load" [loadMovie]
f1: field 150
f2: field 100
f3: field 100
f4: field 100
pad 40x0
button "Quit" [quit]
return
canvas: base iSize img
return
sl: slider 615 [
n: nImages - 1
currentImg: to-integer (sl/data * n) + 1
readImage currentImg]
bt: base 20x20 black on-time [readAllImages]
return
button "<<" [currentImg: 1 readImage currentImg sl/data: 0%]
button ">>" [currentImg: nImages readImage currentImg updateSlider]
button "<" [if currentImg > 1 [currentImg: currentImg - 1 readImage currentImg]
updateSlider]
button ">" [if currentImg < nImages [currentImg: currentImg + 1 readImage currentImg]
updateSlider]
onoff: button "Start/Stop" on-click [
if isFile [
either bt/rate = none [bt/color: green bt/rate: freq]
[bt/color: black bt/rate: none]
]
]
f5: field 75
f6: field 190
do [bt/rate: none]
]
Aucun commentaire:
Enregistrer un commentaire