Documentation
¶
Overview ¶
Package videoio provides a small, standard-library-only "video" layer for the cv image-processing toolkit (github.com/malcolmston/opencv). It reads and writes sequences of frames using codecs that ship with the Go standard library, so it builds and runs anywhere the Go toolchain does — with no cgo and no third-party dependencies.
A clip is treated as a short sequence of frames, each carrying a delay measured in hundredths of a second (centiseconds). Several containers are supported, all built on standard-library codecs:
- Animated GIF (image/gif) — palette-limited, via ReadGIF/WriteGIF, the configurable PalettedGIFWriter (choice of PaletteWebSafe, PalettePlan9 or an AdaptivePalette) and per-frame-delay WriteGIFDelays.
- Animated PNG / APNG (image/png plus a hand-rolled chunk layer) — a true lossless format, via ReadAPNG/WriteAPNG, APNGWriter and WriteAPNGDelays. A real acTL/fcTL/fdAT chunk stream is assembled and parsed, and plain PNGs decode as a single frame.
- Motion-JPEG AVI (image/jpeg inside a real RIFF/AVI container) — via ReadMJPEGAVI/WriteMJPEGAVI and AVIWriter. The writer emits a genuine RIFF structure (avih, strh, strf BITMAPINFOHEADER, movi, idx1) of concatenated JPEG frames; the reader walks it back.
- Numbered image sequences (frame0001.png, …) — via ImageSequenceWriter, ReadImageSequence/WriteImageSequence and OpenImageSequence.
WriteVideoFromMats and ReadVideoToMats dispatch to the right container by file extension.
Captures, properties and seeking ¶
Every reader materialises its frames into a VideoCapture, which offers the OpenCV-style property model: VideoCapture.Get and VideoCapture.Set read and write CAP_PROP_* values (FPS, frame count, width, height, position), VideoCapture.Grab/VideoCapture.Retrieve mirror OpenCV's two-step read, and VideoCapture.SetPosFrames seeks. OpenGIF, OpenAPNG, OpenAVI and OpenImageSequence all return one and satisfy the FrameGrabber interface. ResampleFrames and ResampleCapture retime a clip from one frame rate to another by nearest-frame sampling.
Relationship to OpenCV ¶
The names mirror OpenCV's video I/O API so the code reads familiarly: VideoCapture pulls decoded frames one at a time, and VideoWriter accumulates frames and encodes them on release. Unlike OpenCV, which relies on ffmpeg and a pile of native codecs to open MP4/AVI/etc., this package is deliberately limited to what the pure-Go standard library can do. Real compressed-video codecs (H.264, VP9, …) require cgo bindings to ffmpeg or a similar library and remain out of scope.
Frames as Mats ¶
Every frame crosses the boundary as a cv.Mat. Decoding composites the GIF's (possibly partial, possibly disposed) sub-images onto a full-size canvas and converts the result with cv.FromImage, yielding three-channel RGB Mats. Encoding converts each Mat back to an image with cv.Mat.ToImage and quantizes it to a 256-colour palette. Because frame sizes in a clip must agree, the writer adopts the bounds of the first frame it receives and places every later frame at the origin of that canvas, clipping any overflow.
Quantization and fidelity ¶
GIF is a paletted format: each frame may use at most 256 distinct colours. This package quantizes every frame to the fixed 216-colour web-safe palette (palette.WebSafe) by nearest-colour mapping, without dithering, so the output is fully deterministic — the same frames always produce byte-identical files. The cost is colour fidelity: each channel of every pixel may shift by up to roughly 26 levels toward the nearest palette entry. Callers that need exact colour should keep the original Mats; the GIF round-trip is lossy by construction.
Typical use ¶
Read an animated GIF and iterate its frames:
cap, err := videoio.OpenGIF("clip.gif")
if err != nil {
log.Fatal(err)
}
defer cap.Close()
for {
frame, ok := cap.Read()
if !ok {
break
}
_ = frame // a *cv.Mat
}
Write a sequence of Mats to an animated GIF at 10 frames per second (delay = 10 centiseconds per frame):
w, err := videoio.NewGIFWriter("out.gif", 10)
if err != nil {
log.Fatal(err)
}
for _, m := range frames {
if err := w.Write(m); err != nil {
log.Fatal(err)
}
}
if err := w.Release(); err != nil {
log.Fatal(err)
}
The convenience functions ReadGIF and WriteGIF wrap the whole read or write in a single call when streaming is not required.
Index ¶
- Variables
- func AdaptivePalette(frames []*cv.Mat, maxColors int) color.Palette
- func FourCC(a, b, c, d byte) uint32
- func FourCCString(code uint32) string
- func ReadAPNG(path string) ([]*cv.Mat, []int, error)
- func ReadGIF(path string) ([]*cv.Mat, []int, error)
- func ReadImageSequence(dir, pattern string, start int) ([]*cv.Mat, error)
- func ReadMJPEGAVI(path string) ([]*cv.Mat, float64, error)
- func ReadVideoToMats(path string) ([]*cv.Mat, float64, error)
- func ResampleFrames(frames []*cv.Mat, delaysCentis []int, targetFPS float64) ([]*cv.Mat, []int)
- func WriteAPNG(path string, frames []*cv.Mat, delayCentis int) error
- func WriteAPNGDelays(path string, frames []*cv.Mat, delays []int) error
- func WriteGIF(path string, frames []*cv.Mat, delayCentis int) error
- func WriteGIFDelays(path string, frames []*cv.Mat, delays []int, pal color.Palette, loopCount int) error
- func WriteImageSequence(dir, pattern string, frames []*cv.Mat, start int) ([]string, error)
- func WriteMJPEGAVI(path string, frames []*cv.Mat, fps float64) error
- func WriteVideoFromMats(path string, frames []*cv.Mat, fps float64) error
- type APNGWriter
- type AVIWriter
- type FrameGrabber
- type ImageSequenceWriter
- type PalettedGIFWriter
- type PropID
- type VideoCapture
- func OpenAPNG(path string) (*VideoCapture, error)
- func OpenAVI(path string) (*VideoCapture, error)
- func OpenGIF(path string) (*VideoCapture, error)
- func OpenImageSequence(dir, pattern string, start, delayCentis int) (*VideoCapture, error)
- func ResampleCapture(c *VideoCapture, targetFPS float64) *VideoCapture
- func (c *VideoCapture) Close() error
- func (c *VideoCapture) Delays() []int
- func (c *VideoCapture) FrameCount() int
- func (c *VideoCapture) Frames() []*cv.Mat
- func (c *VideoCapture) Get(prop PropID) float64
- func (c *VideoCapture) Grab() bool
- func (c *VideoCapture) PosFrames() int
- func (c *VideoCapture) Read() (*cv.Mat, bool)
- func (c *VideoCapture) Retrieve() (*cv.Mat, bool)
- func (c *VideoCapture) Set(prop PropID, value float64) bool
- func (c *VideoCapture) SetPosFrames(n int) int
- type VideoWriter
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var PalettePlan9 = color.Palette(palette.Plan9)
PalettePlan9 is the 256-colour palette from the Plan 9 operating system. It covers the colour cube more evenly than the web-safe palette and usually reproduces photographic frames with less error, at the cost of using all 256 slots.
var PaletteWebSafe = color.Palette(palette.WebSafe)
PaletteWebSafe is the deterministic 216-colour web-safe palette used by the default GIF writer. Its channel levels are spaced 51 apart, bounding the nearest-colour error to about 26 per channel.
Functions ¶
func AdaptivePalette ¶ added in v0.4.0
AdaptivePalette builds a palette of at most maxColors entries tailored to the given frames, using popularity quantization: every pixel is binned into a coarse RGB cube (4 bits per channel), the most populous bins are chosen, and each bin contributes its centre colour. The result is deterministic — the same frames and maxColors always yield the same palette — and is a genuine improvement over a fixed palette for footage with a limited colour range. maxColors is clamped to [1, 256]. A transparent slot is not reserved.
func FourCC ¶ added in v0.4.0
FourCC packs four ASCII characters into a 32-bit code the way video containers identify codecs (for example 'M','J','P','G'). The result matches OpenCV's VideoWriter::fourcc and the byte order used in AVI stream headers: the first character occupies the least-significant byte.
func FourCCString ¶ added in v0.4.0
FourCCString unpacks a code produced by FourCC back into its four characters.
func ReadAPNG ¶ added in v0.4.0
ReadAPNG decodes the APNG (or plain PNG) at path and returns every frame as a three-channel RGB Mat together with the matching per-frame delays in centiseconds. Frame offsets and the APNG dispose/blend operations are honoured by compositing onto a full-size canvas, so the returned Mats are complete canvas-sized frames. A plain, non-animated PNG decodes as a single frame.
func ReadGIF ¶
ReadGIF decodes the GIF at path and returns every frame as a Mat together with the matching per-frame delays in centiseconds. Partial frames and GIF disposal methods are honoured: each stored sub-image is composited onto a full-size canvas so the returned Mats are complete, canvas-sized frames. The frames are three-channel RGB.
func ReadImageSequence ¶ added in v0.4.0
ReadImageSequence loads a numbered image sequence from dir. It reads files named by pattern starting at index start and stops at the first index whose file is missing, so the sequence must be contiguous. Each file is decoded via cv.ImRead. It errors if no file exists at the starting index or the pattern is invalid.
func ReadMJPEGAVI ¶ added in v0.4.0
ReadMJPEGAVI parses the MJPEG AVI at path, JPEG-decodes every frame in its movi list into a three-channel RGB Mat, and returns the frames together with the playback rate recorded in the file header. It walks the RIFF structure directly rather than trusting the idx1 index.
func ReadVideoToMats ¶ added in v0.4.0
ReadVideoToMats decodes the clip at path, choosing the container from the file extension (".gif", ".png"/".apng" or ".avi"), and returns every frame as a Mat together with the clip's frame rate in frames per second. It errors on an unrecognised extension or a decode failure.
func ResampleFrames ¶ added in v0.4.0
ResampleFrames resamples a clip to a constant target frame rate. The input is a sequence of frames with per-frame durations in centiseconds; the output is a new sequence, sampled at targetFPS by nearest-frame selection, that spans the same total duration and whose frames all share the delay 100/targetFPS centiseconds. This both up-samples (repeating frames) and down-samples (dropping them), which is how a clip is retimed from one playback rate to another. The returned Mats are shared with the input (no deep copy). It panics if len(frames) != len(delaysCentis) or targetFPS <= 0.
Example ¶
ExampleResampleFrames retimes a two-frame clip to a higher frame rate.
package main
import (
"fmt"
cv "github.com/malcolmston/opencv"
"github.com/malcolmston/opencv/videoio"
)
func main() {
frames := []*cv.Mat{cv.NewMat(2, 2, 3), cv.NewMat(2, 2, 3)}
delays := []int{50, 50} // half a second each: one second total
out, outDelays := videoio.ResampleFrames(frames, delays, 10)
fmt.Printf("%d frames, %d cs each\n", len(out), outDelays[0])
}
Output: 10 frames, 10 cs each
func WriteAPNG ¶ added in v0.4.0
WriteAPNG encodes frames into an APNG at path, giving every frame the same delay of delayCentis centiseconds. It is a convenience wrapper around NewAPNGWriter. The canvas is taken from the first frame; later frames are placed at the origin and clipped. It errors if frames is empty.
Example ¶
ExampleWriteAPNG encodes frames to a lossless animated PNG and reads them back.
dir, err := makeExampleDir()
if err != nil {
log.Fatal(err)
}
path := filepath.Join(dir, "anim.png")
frames := make([]*cv.Mat, 3)
for i := range frames {
m := cv.NewMat(4, 4, 3)
m.SetTo(uint8(i * 60))
frames[i] = m
}
if err := videoio.WriteAPNG(path, frames, 8); err != nil {
log.Fatal(err)
}
got, delays, err := videoio.ReadAPNG(path)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%d frames, delay %d cs, exact=%v\n",
len(got), delays[0], got[0].At(0, 0, 0) == frames[0].At(0, 0, 0))
Output: 3 frames, delay 8 cs, exact=true
func WriteAPNGDelays ¶ added in v0.4.0
WriteAPNGDelays encodes frames into an APNG at path with an independent delay (in centiseconds) for each frame, enabling variable-rate playback. len(delays) must equal len(frames). It errors if frames is empty or the lengths differ.
func WriteGIF ¶
WriteGIF encodes frames into an animated GIF at path, giving every frame the same delay of delayCentis centiseconds. It is a convenience wrapper around NewGIFWriter, VideoWriter.Write and VideoWriter.Release. The output size is taken from the first frame; later frames are placed at the origin and clipped. It returns an error if frames is empty.
Example ¶
ExampleWriteGIF encodes a few solid-colour frames to an animated GIF and reads them back, showing the basic write-then-read round trip.
package main
import (
"fmt"
"log"
"os"
"path/filepath"
cv "github.com/malcolmston/opencv"
"github.com/malcolmston/opencv/videoio"
)
// makeExampleDir returns a fresh temporary directory for example output.
func makeExampleDir() (string, error) {
return os.MkdirTemp("", "videoio-example-")
}
func main() {
dir, err := makeExampleDir()
if err != nil {
log.Fatal(err)
}
path := filepath.Join(dir, "solid.gif")
frames := make([]*cv.Mat, 3)
for i := range frames {
m := cv.NewMat(4, 4, 3)
m.SetTo(uint8(i * 80)) // a distinct grey per frame
frames[i] = m
}
if err := videoio.WriteGIF(path, frames, 10); err != nil {
log.Fatal(err)
}
got, delays, err := videoio.ReadGIF(path)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%d frames, delay %d cs\n", len(got), delays[0])
}
Output: 3 frames, delay 10 cs
func WriteGIFDelays ¶ added in v0.4.0
func WriteGIFDelays(path string, frames []*cv.Mat, delays []int, pal color.Palette, loopCount int) error
WriteGIFDelays encodes frames to an animated GIF at path with an independent per-frame delay (centiseconds) and a chosen palette and loop count. It is the batch counterpart to PalettedGIFWriter. len(delays) must equal len(frames). A nil palette defaults to PaletteWebSafe.
func WriteImageSequence ¶ added in v0.4.0
WriteImageSequence writes every frame to dir using pattern, numbering them from start, and returns the paths written. It is the batch counterpart to ImageSequenceWriter. It errors if frames is empty or the pattern is invalid.
Example ¶
ExampleWriteImageSequence saves frames as numbered PNG files and reloads them.
dir, err := makeExampleDir()
if err != nil {
log.Fatal(err)
}
frames := []*cv.Mat{cv.NewMat(2, 2, 3), cv.NewMat(2, 2, 3), cv.NewMat(2, 2, 3)}
if _, err := videoio.WriteImageSequence(dir, "frame%03d.png", frames, 0); err != nil {
log.Fatal(err)
}
got, err := videoio.ReadImageSequence(dir, "frame%03d.png", 0)
if err != nil {
log.Fatal(err)
}
fmt.Printf("round-tripped %d frames\n", len(got))
Output: round-tripped 3 frames
func WriteMJPEGAVI ¶ added in v0.4.0
WriteMJPEGAVI encodes frames into a Motion-JPEG AVI at path, played back at fps frames per second. It is the batch counterpart to AVIWriter and errors if frames is empty.
Example ¶
ExampleWriteMJPEGAVI writes a Motion-JPEG AVI and reports its frame rate.
dir, err := makeExampleDir()
if err != nil {
log.Fatal(err)
}
path := filepath.Join(dir, "clip.avi")
frames := []*cv.Mat{cv.NewMat(8, 8, 3), cv.NewMat(8, 8, 3)}
if err := videoio.WriteMJPEGAVI(path, frames, 30); err != nil {
log.Fatal(err)
}
got, fps, err := videoio.ReadMJPEGAVI(path)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%d frames at %.0f fps\n", len(got), fps)
Output: 2 frames at 30 fps
func WriteVideoFromMats ¶ added in v0.4.0
WriteVideoFromMats encodes frames to path, choosing the container from the file extension: ".gif" writes an animated GIF, ".png"/".apng" writes an APNG, and ".avi" writes a Motion-JPEG AVI. fps sets the playback rate (converted to per-frame delays for the paletted formats). It errors on an empty frame list or an unrecognised extension.
Types ¶
type APNGWriter ¶ added in v0.4.0
type APNGWriter struct {
// contains filtered or unexported fields
}
APNGWriter accumulates frames and encodes them as a single Animated PNG when released. Unlike GIF, APNG is a true-colour format, so frames are written without palette quantization and survive the round trip pixel-for-pixel. The canvas size is fixed by the first frame; later frames are composited at the origin and clipped to those bounds. The zero value is not usable — obtain one from NewAPNGWriter.
func NewAPNGWriter ¶ added in v0.4.0
func NewAPNGWriter(path string, delayCentis int) (*APNGWriter, error)
NewAPNGWriter creates a writer that encodes every frame passed to APNGWriter.Write into an APNG file at path when APNGWriter.Release runs. delayCentis is each frame's display duration in centiseconds; a non-positive value is clamped to 0. The animation loops forever; use APNGWriter.SetLoopCount to bound it. No file is created until Release.
func (*APNGWriter) Release ¶ added in v0.4.0
func (w *APNGWriter) Release() error
Release encodes every written frame into the destination APNG and finalizes the writer. Each frame is stored full-size with dispose "none" and blend "source", so it fully replaces the previous one. Calling Release twice, or on a writer with no frames, returns an error.
func (*APNGWriter) SetLoopCount ¶ added in v0.4.0
func (w *APNGWriter) SetLoopCount(n int)
SetLoopCount limits how many times viewers replay the animation; 0 (the default) means loop forever. It must be called before APNGWriter.Release.
func (*APNGWriter) Write ¶ added in v0.4.0
func (w *APNGWriter) Write(frame *cv.Mat) error
Write appends frame to the animation, using the writer's default per-frame delay. The first frame fixes the canvas size; later frames are drawn at the origin and clipped. It errors on an empty frame or after Release.
func (*APNGWriter) WriteFrame ¶ added in v0.4.0
func (w *APNGWriter) WriteFrame(frame *cv.Mat, delayCentis int) error
WriteFrame appends frame with an explicit display duration of delayCentis centiseconds, overriding the writer's default for this frame only. This is how variable-rate animations are built. It errors on an empty frame or after Release.
type AVIWriter ¶ added in v0.4.0
type AVIWriter struct {
// contains filtered or unexported fields
}
AVIWriter accumulates frames and writes them as a minimal but standards-shaped Motion-JPEG AVI file when released. Every frame is JPEG-encoded and stored as a chunk inside the movi list of a real RIFF/AVI container, complete with the avih main header, a vids/MJPG stream header, a BITMAPINFOHEADER format chunk and an idx1 index — so the result is parseable by this package and by conventional AVI tooling. The zero value is not usable — obtain one from NewAVIWriter.
func NewAVIWriter ¶ added in v0.4.0
NewAVIWriter creates a writer that emits an MJPEG AVI at path when AVIWriter.Release runs, played back at fps frames per second. A non-positive fps is replaced with a sensible default. The frame size is fixed by the first frame written. No file is created until Release.
func (*AVIWriter) Release ¶ added in v0.4.0
Release assembles and writes the AVI file, then finalizes the writer. Calling it twice, or with no frames written, returns an error.
type FrameGrabber ¶ added in v0.4.0
type FrameGrabber interface {
// Grab advances to the next frame without decoding it, reporting whether a
// frame is now available for Retrieve.
Grab() bool
// Retrieve returns the frame selected by the most recent successful Grab.
Retrieve() (*cv.Mat, bool)
// Read grabs and retrieves the next frame in one step.
Read() (*cv.Mat, bool)
// Close releases any resources held by the grabber.
Close() error
}
FrameGrabber is the read side of the video I/O API, modelled on OpenCV's cv::VideoCapture. A grabber yields frames in order through the classic grab/retrieve split — FrameGrabber.Grab advances to the next frame and FrameGrabber.Retrieve decodes the most recently grabbed one — or through the combined FrameGrabber.Read. All frame-backed readers in this package (VideoCapture and everything that returns one, such as OpenGIF, OpenAPNG, OpenAVI and OpenImageSequence) satisfy it.
type ImageSequenceWriter ¶ added in v0.4.0
type ImageSequenceWriter struct {
// contains filtered or unexported fields
}
ImageSequenceWriter saves frames as individually numbered image files in a directory — the classic "frame0001.png, frame0002.png, …" layout OpenCV accepts through a printf-style path. The file format follows the pattern's extension: ".png" writes PNG, ".jpg"/".jpeg" writes JPEG. The zero value is not usable — obtain one from NewImageSequenceWriter.
func NewImageSequenceWriter ¶ added in v0.4.0
func NewImageSequenceWriter(dir, pattern string, index int) (*ImageSequenceWriter, error)
NewImageSequenceWriter creates a writer that places frames in dir. The pattern is a printf template with exactly one integer verb naming each file, for example "frame%04d.png" or "img_%d.jpg"; its extension selects the codec. index is the number assigned to the first frame (commonly 0 or 1). The directory is created if it does not exist.
func (*ImageSequenceWriter) Count ¶ added in v0.4.0
func (w *ImageSequenceWriter) Count() int
Count returns the number of frames written so far.
func (*ImageSequenceWriter) Files ¶ added in v0.4.0
func (w *ImageSequenceWriter) Files() []string
Files returns the paths written so far, in order.
func (*ImageSequenceWriter) Write ¶ added in v0.4.0
func (w *ImageSequenceWriter) Write(frame *cv.Mat) (string, error)
Write encodes frame to the next numbered file and returns its path. Frame numbers advance by one on each successful call. It errors on an empty frame or if the file cannot be written.
type PalettedGIFWriter ¶ added in v0.4.0
type PalettedGIFWriter struct {
// contains filtered or unexported fields
}
PalettedGIFWriter encodes frames to an animated GIF with caller-controlled quantization: any color.Palette may be supplied (a fixed one such as PaletteWebSafe / PalettePlan9, or one built by AdaptivePalette), the loop count is configurable, and each frame carries its own delay. The canvas size is fixed by the first frame. The zero value is not usable — obtain one from NewPalettedGIFWriter.
func NewPalettedGIFWriter ¶ added in v0.4.0
func NewPalettedGIFWriter(path string, pal color.Palette, loopCount int) (*PalettedGIFWriter, error)
NewPalettedGIFWriter creates a GIF writer that maps every frame onto pal. A nil or empty palette falls back to PaletteWebSafe; a palette longer than 256 colours is rejected, matching the GIF format limit. loopCount follows the GIF convention: 0 loops forever, a positive n plays n+1 times total. No file is created until PalettedGIFWriter.Release.
func (*PalettedGIFWriter) Release ¶ added in v0.4.0
func (w *PalettedGIFWriter) Release() error
Release encodes every frame to the destination GIF and finalizes the writer. Calling it twice, or with no frames, returns an error.
func (*PalettedGIFWriter) WriteFrame ¶ added in v0.4.0
func (w *PalettedGIFWriter) WriteFrame(frame *cv.Mat, delayCentis int) error
WriteFrame quantizes frame to the writer's palette and appends it with a display duration of delayCentis centiseconds, so each frame may play for a different length of time. The first frame fixes the canvas; later frames are drawn at the origin and clipped. It errors on an empty frame or after Release.
type PropID ¶ added in v0.4.0
type PropID int
PropID identifies a capture or writer property. The values mirror the numeric constants of OpenCV's cv::VideoCaptureProperties enumeration so that code ported from OpenCV keeps using the same names, and so a property read back from one API can be fed to another unchanged.
const ( // CAP_PROP_POS_MSEC is the presentation timestamp of the next frame to be // decoded, measured in milliseconds from the start of the clip. CAP_PROP_POS_MSEC PropID = 0 // CAP_PROP_POS_FRAMES is the zero-based index of the next frame to decode. // Setting it seeks; see [VideoCapture.SetPosFrames]. CAP_PROP_POS_FRAMES PropID = 1 // CAP_PROP_POS_AVI_RATIO is the relative position within the clip in the // range [0, 1], where 0 is the first frame and 1 is just past the last. CAP_PROP_POS_AVI_RATIO PropID = 2 // CAP_PROP_FRAME_WIDTH is the frame width in pixels. CAP_PROP_FRAME_WIDTH PropID = 3 // CAP_PROP_FRAME_HEIGHT is the frame height in pixels. CAP_PROP_FRAME_HEIGHT PropID = 4 // CAP_PROP_FPS is the nominal frame rate in frames per second. CAP_PROP_FPS PropID = 5 // CAP_PROP_FOURCC is the four-character codec code packed into a float, as // produced by [FourCC]. CAP_PROP_FOURCC PropID = 6 // CAP_PROP_FRAME_COUNT is the total number of frames in the clip. CAP_PROP_FRAME_COUNT PropID = 7 )
Capture and writer property identifiers, matching OpenCV's CAP_PROP_* values. Not every backend honours every property; see VideoCapture.Get, VideoCapture.Set, VideoWriter.Get and VideoWriter.Set for the subset each type supports.
type VideoCapture ¶
type VideoCapture struct {
// contains filtered or unexported fields
}
VideoCapture reads the frames of an animated GIF as a sequence of Mats. All frames are decoded eagerly when the capture is opened, then handed out one at a time by VideoCapture.Read. A VideoCapture holds no operating-system resources once opened; VideoCapture.Close simply releases the decoded frames. The zero value is not usable — obtain one from OpenGIF.
Example ¶
ExampleVideoCapture streams frames from a GIF one at a time.
package main
import (
"fmt"
"log"
"os"
"path/filepath"
cv "github.com/malcolmston/opencv"
"github.com/malcolmston/opencv/videoio"
)
// makeExampleDir returns a fresh temporary directory for example output.
func makeExampleDir() (string, error) {
return os.MkdirTemp("", "videoio-example-")
}
func main() {
dir, err := makeExampleDir()
if err != nil {
log.Fatal(err)
}
path := filepath.Join(dir, "cap.gif")
src := []*cv.Mat{cv.NewMat(2, 2, 3), cv.NewMat(2, 2, 3)}
if err := videoio.WriteGIF(path, src, 5); err != nil {
log.Fatal(err)
}
cap, err := videoio.OpenGIF(path)
if err != nil {
log.Fatal(err)
}
defer cap.Close()
count := 0
for {
if _, ok := cap.Read(); !ok {
break
}
count++
}
fmt.Printf("read %d frames\n", count)
}
Output: read 2 frames
func OpenAPNG ¶ added in v0.4.0
func OpenAPNG(path string) (*VideoCapture, error)
OpenAPNG decodes the APNG at path and returns a VideoCapture over its frames, so an animated PNG can be streamed with the same grab/retrieve API as any other source.
func OpenAVI ¶ added in v0.4.0
func OpenAVI(path string) (*VideoCapture, error)
OpenAVI parses the MJPEG AVI at path and returns a VideoCapture over its frames, with per-frame delays derived from the file's frame rate so that CAP_PROP_FPS reads back correctly.
func OpenGIF ¶
func OpenGIF(path string) (*VideoCapture, error)
OpenGIF opens the animated (or single-frame) GIF at path and decodes every frame into memory. It returns an error if the file cannot be read or does not contain a valid GIF.
func OpenImageSequence ¶ added in v0.4.0
func OpenImageSequence(dir, pattern string, start, delayCentis int) (*VideoCapture, error)
OpenImageSequence reads a numbered sequence with ReadImageSequence and returns a VideoCapture over the frames, so an on-disk frame directory can be streamed like any other source. The delayCentis argument sets the nominal per-frame delay reported through CAP_PROP_FPS and used by re-encoders.
func ResampleCapture ¶ added in v0.4.0
func ResampleCapture(c *VideoCapture, targetFPS float64) *VideoCapture
ResampleCapture resamples a capture's frames to targetFPS and returns a fresh VideoCapture positioned at the start, so a clip opened at one rate can be replayed at another. It reads the source frames and delays as they currently stand; it does not consume or modify the input capture's read position beyond what it needs.
func (*VideoCapture) Close ¶
func (c *VideoCapture) Close() error
Close releases the decoded frames and resets the capture. After Close the capture reports zero frames and VideoCapture.Read returns (nil, false). It never fails and always returns nil; the error result exists to match the idiomatic io.Closer shape.
func (*VideoCapture) Delays ¶
func (c *VideoCapture) Delays() []int
Delays returns the per-frame display durations in centiseconds (hundredths of a second), one entry per frame and in the same order as VideoCapture.Frames.
func (*VideoCapture) FrameCount ¶
func (c *VideoCapture) FrameCount() int
FrameCount returns the total number of decoded frames.
func (*VideoCapture) Frames ¶
func (c *VideoCapture) Frames() []*cv.Mat
Frames returns all decoded frames in order. The returned slice is the capture's own backing slice and shares its Mats; do not mutate it in place. Reading it does not affect the position used by VideoCapture.Read.
func (*VideoCapture) Get ¶ added in v0.4.0
func (c *VideoCapture) Get(prop PropID) float64
Get returns the value of property prop, or 0 if the property is unknown or the capture is empty. Position properties reflect the current read cursor, so they change as frames are consumed.
Example ¶
ExampleVideoCapture_Get reads standard properties from a capture.
dir, err := makeExampleDir()
if err != nil {
log.Fatal(err)
}
path := filepath.Join(dir, "p.gif")
frames := []*cv.Mat{cv.NewMat(6, 8, 3), cv.NewMat(6, 8, 3)}
if err := videoio.WriteGIF(path, frames, 10); err != nil {
log.Fatal(err)
}
cap, err := videoio.OpenGIF(path)
if err != nil {
log.Fatal(err)
}
defer cap.Close()
fmt.Printf("%.0fx%.0f, %.0f frames, %.0f fps\n",
cap.Get(videoio.CAP_PROP_FRAME_WIDTH),
cap.Get(videoio.CAP_PROP_FRAME_HEIGHT),
cap.Get(videoio.CAP_PROP_FRAME_COUNT),
cap.Get(videoio.CAP_PROP_FPS))
Output: 8x6, 2 frames, 10 fps
func (*VideoCapture) Grab ¶ added in v0.4.0
func (c *VideoCapture) Grab() bool
Grab advances the capture to the next frame without copying it out, returning true while frames remain. It pairs with VideoCapture.Retrieve: together they perform the same work as VideoCapture.Read, matching OpenCV's grab/retrieve split. Once the frames are exhausted it returns false.
func (*VideoCapture) PosFrames ¶ added in v0.4.0
func (c *VideoCapture) PosFrames() int
PosFrames returns the zero-based index of the frame that the next VideoCapture.Read or VideoCapture.Grab will return.
func (*VideoCapture) Read ¶
func (c *VideoCapture) Read() (*cv.Mat, bool)
Read returns the next frame and true, advancing the internal position. Once every frame has been returned it yields (nil, false) on every subsequent call. The returned Mat is the capture's own copy; callers that intend to mutate it should cv.Mat.Clone it first.
func (*VideoCapture) Retrieve ¶ added in v0.4.0
func (c *VideoCapture) Retrieve() (*cv.Mat, bool)
Retrieve returns the frame selected by the most recent successful VideoCapture.Grab. It does not advance the position, so calling it twice in a row yields the same frame. Before any Grab, or after the frames are exhausted, it returns (nil, false). The returned Mat is the capture's own copy; clone it before mutating.
func (*VideoCapture) Set ¶ added in v0.4.0
func (c *VideoCapture) Set(prop PropID, value float64) bool
Set writes property prop and reports whether the property is settable on a capture. Only CAP_PROP_POS_FRAMES (seek, see VideoCapture.SetPosFrames), CAP_PROP_POS_AVI_RATIO (fractional seek) and CAP_PROP_FPS (rewrite every frame delay) are honoured; all other properties are read-only and return false.
func (*VideoCapture) SetPosFrames ¶ added in v0.4.0
func (c *VideoCapture) SetPosFrames(n int) int
SetPosFrames seeks so that the next read returns frame n. The index is clamped to the valid range [0, FrameCount]; seeking to FrameCount leaves the capture at end-of-stream. It reports the position actually adopted.
type VideoWriter ¶
type VideoWriter struct {
// contains filtered or unexported fields
}
VideoWriter accumulates frames and encodes them as a single animated GIF when released. Frames are quantized to the web-safe palette as they arrive. The canvas size is fixed by the first frame written: later frames are placed at the origin and clipped to those bounds. The zero value is not usable — obtain one from NewGIFWriter.
func NewGIFWriter ¶
func NewGIFWriter(path string, delayCentis int) (*VideoWriter, error)
NewGIFWriter creates a writer that will encode all frames given to VideoWriter.Write into an animated GIF at path when VideoWriter.Release is called. delayCentis is the display duration of each frame in centiseconds (hundredths of a second); for example 10 yields roughly ten frames per second. A non-positive delayCentis is clamped to 0, which most viewers treat as "as fast as possible". No file is created until Release runs.
func (*VideoWriter) Get ¶ added in v0.4.0
func (w *VideoWriter) Get(prop PropID) float64
Get returns the value of property prop for the writer, or 0 if it is unknown. Supported properties are CAP_PROP_FPS, CAP_PROP_FRAME_COUNT (frames written so far), CAP_PROP_FRAME_WIDTH and CAP_PROP_FRAME_HEIGHT (both fixed by the first frame, 0 before any frame is written).
func (*VideoWriter) Release ¶
func (w *VideoWriter) Release() error
Release encodes every written frame to the destination GIF and finalizes the writer. It uses disposal method "none" for all frames, so each frame is drawn over the previous one, and sets an infinite loop count. Release is idempotent only in that calling it a second time returns an error; a writer with no frames also returns an error. After a successful Release the writer must not be used again.
func (*VideoWriter) Set ¶ added in v0.4.0
func (w *VideoWriter) Set(prop PropID, value float64) bool
Set writes property prop and reports whether it was applied. Only CAP_PROP_FPS is settable, and only before any frame is written; changing the rate mid-stream, or setting any other property, returns false.
func (*VideoWriter) Write ¶
func (w *VideoWriter) Write(frame *cv.Mat) error
Write quantizes frame and appends it to the animation. The first frame fixes the output size; every later frame is drawn at the canvas origin and any part extending past the first frame's bounds is clipped. It returns an error if the frame is empty or the writer has already been released.