rgb to rgba
This commit is contained in:
parent
2f07344cb8
commit
f6eaee9f1f
6 changed files with 51 additions and 59 deletions
|
@ -2,7 +2,7 @@
|
|||
|
||||
A simple Video I/O library written in Go. This library relies on [FFmpeg](https://www.ffmpeg.org/), and [FFProbe](https://www.ffmpeg.org/) which must be downloaded before usage and added to the system path.
|
||||
|
||||
All frames are encoded and decoded in 8-bit RGB format.
|
||||
All frames are encoded and decoded in 8-bit RGBA format.
|
||||
|
||||
For Audio I/O using FFmpeg, see the [`aio`](https://github.com/AlexEidt/aio) project.
|
||||
|
||||
|
@ -16,7 +16,7 @@ go get github.com/AlexEidt/Vidio
|
|||
|
||||
The `Video` struct stores data about a video file you give it. The code below shows an example of sequentially reading the frames of the given video.
|
||||
|
||||
Calling the `Read()` function will fill in the `Video` struct `framebuffer` with the next frame data as 8-bit RGB data, stored in a flattened byte array in row-major order where each pixel is represented by three consecutive bytes representing the R, G and B component of that pixel.
|
||||
Calling the `Read()` function will fill in the `Video` struct `framebuffer` with the next frame data as 8-bit RGBA data, stored in a flattened byte array in row-major order where each pixel is represented by four consecutive bytes representing the R, G, B and A components of that pixel. Note that the A (alpha) component will always be 255.
|
||||
|
||||
```go
|
||||
vidio.NewVideo(filename string) (*vidio.Video, error)
|
||||
|
@ -81,7 +81,6 @@ Macro() int
|
|||
FPS() float64
|
||||
Quality() float64
|
||||
Codec() string
|
||||
Format() string
|
||||
|
||||
Write(frame []byte) error
|
||||
Close()
|
||||
|
@ -96,7 +95,6 @@ type Options struct {
|
|||
FPS float64 // Frames per second for output video.
|
||||
Quality float64 // If bitrate not given, use quality instead. Must be between 0 and 1. 0:best, 1:worst.
|
||||
Codec string // Codec for video.
|
||||
Format string // Pixel Format for video. Default "rgb24".
|
||||
StreamFile string // File path for extra stream data.
|
||||
}
|
||||
```
|
||||
|
|
|
@ -90,7 +90,7 @@ func NewCamera(stream int) (*Camera, error) {
|
|||
return nil, fmt.Errorf("unsupported OS: %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
camera := &Camera{name: device, depth: 3}
|
||||
camera := &Camera{name: device, depth: 4}
|
||||
if err := camera.getCameraData(device); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ func (camera *Camera) init() error {
|
|||
"-f", webcamDeviceName,
|
||||
"-i", camera.name,
|
||||
"-f", "image2pipe",
|
||||
"-pix_fmt", "rgb24",
|
||||
"-pix_fmt", "rgba",
|
||||
"-vcodec", "rawvideo",
|
||||
"-",
|
||||
)
|
||||
|
|
13
imageio.go
13
imageio.go
|
@ -11,7 +11,7 @@ import (
|
|||
"image/png"
|
||||
)
|
||||
|
||||
// Reads an image from a file. Currently only supports png and jpeg.
|
||||
// Reads an image into an rgba byte buffer from a file. Currently only supports png and jpeg.
|
||||
func Read(filename string, buffer ...[]byte) (int, int, []byte, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
|
@ -25,7 +25,7 @@ func Read(filename string, buffer ...[]byte) (int, int, []byte, error) {
|
|||
}
|
||||
|
||||
bounds := image.Bounds().Max
|
||||
size := bounds.X * bounds.Y * 3
|
||||
size := bounds.X * bounds.Y * 4
|
||||
|
||||
var data []byte
|
||||
if len(buffer) > 0 {
|
||||
|
@ -45,13 +45,14 @@ func Read(filename string, buffer ...[]byte) (int, int, []byte, error) {
|
|||
data[index+0] = byte(r)
|
||||
data[index+1] = byte(g)
|
||||
data[index+2] = byte(b)
|
||||
index += 3
|
||||
data[index+3] = 255
|
||||
index += 4
|
||||
}
|
||||
}
|
||||
return bounds.X, bounds.Y, data, nil
|
||||
}
|
||||
|
||||
// Writes an image to a file. Currently only supports png and jpeg.
|
||||
// Writes a rgba byte buffer to a file. Currently only supports png and jpeg.
|
||||
func Write(filename string, width, height int, buffer []byte) error {
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
|
@ -63,9 +64,9 @@ func Write(filename string, width, height int, buffer []byte) error {
|
|||
index := 0
|
||||
for h := 0; h < height; h++ {
|
||||
for w := 0; w < width; w++ {
|
||||
r, g, b := buffer[index], buffer[index+1], buffer[index+2]
|
||||
r, g, b := buffer[index+0], buffer[index+1], buffer[index+2]
|
||||
image.Set(w, h, color.RGBA{r, g, b, 255})
|
||||
index += 3
|
||||
index += 4
|
||||
}
|
||||
}
|
||||
|
||||
|
|
6
video.go
6
video.go
|
@ -143,7 +143,7 @@ func NewVideoStreams(filename string) ([]*Video, error) {
|
|||
for i, data := range videoData {
|
||||
video := &Video{
|
||||
filename: filename,
|
||||
depth: 3,
|
||||
depth: 4,
|
||||
stream: i,
|
||||
hasstreams: hasstream,
|
||||
metadata: data,
|
||||
|
@ -190,13 +190,13 @@ func (video *Video) addVideoData(data map[string]string) {
|
|||
func (video *Video) init() error {
|
||||
// If user exits with Ctrl+C, stop ffmpeg process.
|
||||
video.cleanup()
|
||||
// ffmpeg command to pipe video data to stdout in 8-bit RGB format.
|
||||
// ffmpeg command to pipe video data to stdout in 8-bit RGBA format.
|
||||
cmd := exec.Command(
|
||||
"ffmpeg",
|
||||
"-i", video.filename,
|
||||
"-f", "image2pipe",
|
||||
"-loglevel", "quiet",
|
||||
"-pix_fmt", "rgb24",
|
||||
"-pix_fmt", "rgba",
|
||||
"-vcodec", "rawvideo",
|
||||
"-map", fmt.Sprintf("0:v:%d", video.stream),
|
||||
"-",
|
||||
|
|
|
@ -23,7 +23,6 @@ type VideoWriter struct {
|
|||
fps float64 // Frames per second for output video. Default 25.
|
||||
quality float64 // Used if bitrate not given. Default 0.5.
|
||||
codec string // Codec to encode video with. Default libx264.
|
||||
format string // Output format. Default rgb24.
|
||||
pipe *io.WriteCloser // Stdout pipe of ffmpeg process.
|
||||
cmd *exec.Cmd // ffmpeg command.
|
||||
}
|
||||
|
@ -37,7 +36,6 @@ type Options struct {
|
|||
FPS float64 // Frames per second for output video.
|
||||
Quality float64 // If bitrate not given, use quality instead. Must be between 0 and 1. 0:best, 1:worst.
|
||||
Codec string // Codec for video.
|
||||
Format string // Pixel Format for video. Default "rgb24".
|
||||
StreamFile string // File path for extra stream data.
|
||||
}
|
||||
|
||||
|
@ -86,10 +84,6 @@ func (writer *VideoWriter) Codec() string {
|
|||
return writer.codec
|
||||
}
|
||||
|
||||
func (writer *VideoWriter) Format() string {
|
||||
return writer.format
|
||||
}
|
||||
|
||||
// Creates a new VideoWriter struct with default values from the Options struct.
|
||||
func NewVideoWriter(filename string, width, height int, options *Options) (*VideoWriter, error) {
|
||||
// Check if ffmpeg is installed on the users machine.
|
||||
|
@ -149,12 +143,6 @@ func NewVideoWriter(filename string, width, height int, options *Options) (*Vide
|
|||
writer.codec = options.Codec
|
||||
}
|
||||
|
||||
if options.Format == "" {
|
||||
writer.format = "rgb24"
|
||||
} else {
|
||||
writer.format = options.Format
|
||||
}
|
||||
|
||||
if options.StreamFile != "" {
|
||||
if !exists(options.StreamFile) {
|
||||
return nil, fmt.Errorf("file %s does not exist", options.StreamFile)
|
||||
|
@ -177,7 +165,7 @@ func (writer *VideoWriter) init() error {
|
|||
"-f", "rawvideo",
|
||||
"-vcodec", "rawvideo",
|
||||
"-s", fmt.Sprintf("%dx%d", writer.width, writer.height), // frame w x h.
|
||||
"-pix_fmt", writer.format,
|
||||
"-pix_fmt", "rgba",
|
||||
"-r", fmt.Sprintf("%.02f", writer.fps), // frames per second.
|
||||
"-i", "-", // The input comes from stdin.
|
||||
}
|
||||
|
@ -206,7 +194,7 @@ func (writer *VideoWriter) init() error {
|
|||
command = append(
|
||||
command,
|
||||
"-vcodec", writer.codec,
|
||||
"-pix_fmt", "yuv420p", // Output is 8-bit RGB, no alpha.
|
||||
"-pix_fmt", "yuv420p", // Output is 8-bit RGB, ignore alpha.
|
||||
)
|
||||
|
||||
// Code from the imageio-ffmpeg project.
|
||||
|
@ -246,6 +234,8 @@ func (writer *VideoWriter) init() error {
|
|||
if writer.height%writer.macro > 0 {
|
||||
height += writer.macro - (writer.height % writer.macro)
|
||||
}
|
||||
writer.width = width
|
||||
writer.height = height
|
||||
command = append(
|
||||
command,
|
||||
"-vf", fmt.Sprintf("scale=%d:%d", width, height),
|
||||
|
|
|
@ -39,7 +39,7 @@ func TestVideoMetaData(t *testing.T) {
|
|||
assertEquals(video.filename, "test/koala.mp4")
|
||||
assertEquals(video.width, 480)
|
||||
assertEquals(video.height, 270)
|
||||
assertEquals(video.depth, 3)
|
||||
assertEquals(video.depth, 4)
|
||||
assertEquals(video.bitrate, 170549)
|
||||
assertEquals(video.frames, 101)
|
||||
assertEquals(video.duration, 3.366667)
|
||||
|
@ -67,17 +67,23 @@ func TestVideoFrame(t *testing.T) {
|
|||
defer video.Close()
|
||||
|
||||
video.Read()
|
||||
// [203 222 134 203 222 134 203 222 134 203]
|
||||
// [203 222 134 255 203 222 134 255 203 222 134 255 203]
|
||||
assertEquals(video.framebuffer[0], uint8(203))
|
||||
assertEquals(video.framebuffer[1], uint8(222))
|
||||
assertEquals(video.framebuffer[2], uint8(134))
|
||||
assertEquals(video.framebuffer[3], uint8(203))
|
||||
assertEquals(video.framebuffer[4], uint8(222))
|
||||
assertEquals(video.framebuffer[5], uint8(134))
|
||||
assertEquals(video.framebuffer[6], uint8(203))
|
||||
assertEquals(video.framebuffer[7], uint8(222))
|
||||
assertEquals(video.framebuffer[8], uint8(134))
|
||||
assertEquals(video.framebuffer[9], uint8(203))
|
||||
assertEquals(video.framebuffer[3], uint8(255))
|
||||
|
||||
assertEquals(video.framebuffer[4], uint8(203))
|
||||
assertEquals(video.framebuffer[5], uint8(222))
|
||||
assertEquals(video.framebuffer[6], uint8(134))
|
||||
assertEquals(video.framebuffer[7], uint8(255))
|
||||
|
||||
assertEquals(video.framebuffer[8], uint8(203))
|
||||
assertEquals(video.framebuffer[9], uint8(222))
|
||||
assertEquals(video.framebuffer[10], uint8(134))
|
||||
assertEquals(video.framebuffer[11], uint8(255))
|
||||
|
||||
assertEquals(video.framebuffer[12], uint8(203))
|
||||
|
||||
fmt.Println("Video Frame Test Passed")
|
||||
}
|
||||
|
@ -90,8 +96,8 @@ func TestVideoWriting(t *testing.T) {
|
|||
}
|
||||
options := Options{
|
||||
FPS: video.FPS(),
|
||||
Bitrate: video.Bitrate(),
|
||||
Codec: video.Codec(),
|
||||
Bitrate: video.Bitrate(),
|
||||
}
|
||||
if video.HasStreams() {
|
||||
options.StreamFile = video.FileName()
|
||||
|
@ -101,12 +107,11 @@ func TestVideoWriting(t *testing.T) {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for video.Read() {
|
||||
err := writer.Write(video.FrameBuffer())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
writer.Write(video.FrameBuffer())
|
||||
}
|
||||
|
||||
writer.Close()
|
||||
|
||||
os.Remove(output)
|
||||
|
@ -134,14 +139,6 @@ func TestCameraIO(t *testing.T) {
|
|||
count := 0
|
||||
for webcam.Read() {
|
||||
frame := webcam.FrameBuffer()
|
||||
for i := 0; i < len(frame); i += 3 {
|
||||
rgb := frame[i : i+3]
|
||||
r, g, b := int(rgb[0]), int(rgb[1]), int(rgb[2])
|
||||
gray := uint8((3*r + 4*g + b) / 8)
|
||||
frame[i] = gray
|
||||
frame[i+1] = gray
|
||||
frame[i+2] = gray
|
||||
}
|
||||
err := writer.Write(frame)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -250,18 +247,24 @@ func TestImageRead(t *testing.T) {
|
|||
|
||||
assertEquals(w, 200)
|
||||
assertEquals(h, 133)
|
||||
assertEquals(len(img), 200*133*3)
|
||||
// [255 221 189 255 221 189 255 222 186 255]
|
||||
assertEquals(len(img), 200*133*4)
|
||||
// [255 221 189 255 255 221 189 255 255 222 186 255 255]
|
||||
assertEquals(img[0], uint8(255))
|
||||
assertEquals(img[1], uint8(221))
|
||||
assertEquals(img[2], uint8(189))
|
||||
assertEquals(img[3], uint8(255))
|
||||
assertEquals(img[4], uint8(221))
|
||||
assertEquals(img[5], uint8(189))
|
||||
assertEquals(img[6], uint8(255))
|
||||
assertEquals(img[7], uint8(222))
|
||||
assertEquals(img[8], uint8(186))
|
||||
assertEquals(img[9], uint8(255))
|
||||
|
||||
assertEquals(img[4], uint8(255))
|
||||
assertEquals(img[5], uint8(221))
|
||||
assertEquals(img[6], uint8(189))
|
||||
assertEquals(img[7], uint8(255))
|
||||
|
||||
assertEquals(img[8], uint8(255))
|
||||
assertEquals(img[9], uint8(222))
|
||||
assertEquals(img[10], uint8(186))
|
||||
assertEquals(img[11], uint8(255))
|
||||
|
||||
assertEquals(img[12], uint8(255))
|
||||
|
||||
fmt.Println("Image Reading Test Passed")
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue