mirror of
https://github.com/u2takey/ffmpeg-go.git
synced 2025-10-05 16:06:52 +08:00
add example for gocv
This commit is contained in:
@@ -1,112 +0,0 @@
|
|||||||
// +build gocv
|
|
||||||
|
|
||||||
package examples
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"image"
|
|
||||||
"image/color"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
ffmpeg "github.com/u2takey/ffmpeg-go"
|
|
||||||
"gocv.io/x/gocv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestExampleOpenCvFaceDetect(t *testing.T) {
|
|
||||||
ExampleFaceDetection("./sample_data/head-pose-face-detection-male-short.mp4",
|
|
||||||
"./sample_data/haarcascade_frontalface_default.xml")
|
|
||||||
}
|
|
||||||
|
|
||||||
func readProcess(infileName string, writer io.WriteCloser) <-chan error {
|
|
||||||
log.Println("Starting ffmpeg process1")
|
|
||||||
done := make(chan error)
|
|
||||||
go func() {
|
|
||||||
err := ffmpeg.Input(infileName).
|
|
||||||
Output("pipe:",
|
|
||||||
ffmpeg.KwArgs{
|
|
||||||
"format": "rawvideo", "pix_fmt": "rgb24",
|
|
||||||
}).
|
|
||||||
WithOutput(writer).
|
|
||||||
Run()
|
|
||||||
log.Println("ffmpeg process1 done")
|
|
||||||
_ = writer.Close()
|
|
||||||
done <- err
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
return done
|
|
||||||
}
|
|
||||||
|
|
||||||
func openCvProcess(xmlFile string, reader io.ReadCloser, w, h int) {
|
|
||||||
// open display window
|
|
||||||
window := gocv.NewWindow("Face Detect")
|
|
||||||
defer window.Close()
|
|
||||||
|
|
||||||
// color for the rect when faces detected
|
|
||||||
blue := color.RGBA{0, 0, 255, 0}
|
|
||||||
|
|
||||||
classifier := gocv.NewCascadeClassifier()
|
|
||||||
defer classifier.Close()
|
|
||||||
|
|
||||||
if !classifier.Load(xmlFile) {
|
|
||||||
fmt.Printf("Error reading cascade file: %v\n", xmlFile)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
frameSize := w * h * 3
|
|
||||||
buf := make([]byte, frameSize, frameSize)
|
|
||||||
for {
|
|
||||||
n, err := io.ReadFull(reader, buf)
|
|
||||||
if n == 0 || err == io.EOF {
|
|
||||||
return
|
|
||||||
} else if n != frameSize || err != nil {
|
|
||||||
panic(fmt.Sprintf("read error: %d, %s", n, err))
|
|
||||||
}
|
|
||||||
img, err := gocv.NewMatFromBytes(h, w, gocv.MatTypeCV8UC3, buf)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("decode fail", err)
|
|
||||||
}
|
|
||||||
if img.Empty() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
img2 := gocv.NewMat()
|
|
||||||
gocv.CvtColor(img, &img2, gocv.ColorBGRToRGB)
|
|
||||||
|
|
||||||
// detect faces
|
|
||||||
rects := classifier.DetectMultiScale(img2)
|
|
||||||
fmt.Printf("found %d faces\n", len(rects))
|
|
||||||
|
|
||||||
// draw a rectangle around each face on the original image,
|
|
||||||
// along with text identifing as "Human"
|
|
||||||
for _, r := range rects {
|
|
||||||
gocv.Rectangle(&img2, r, blue, 3)
|
|
||||||
|
|
||||||
size := gocv.GetTextSize("Human", gocv.FontHersheyPlain, 1.2, 2)
|
|
||||||
pt := image.Pt(r.Min.X+(r.Min.X/2)-(size.X/2), r.Min.Y-2)
|
|
||||||
gocv.PutText(&img2, "Human", pt, gocv.FontHersheyPlain, 1.2, blue, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// show the image in the window, and wait 1 millisecond
|
|
||||||
window.IMShow(img2)
|
|
||||||
img.Close()
|
|
||||||
img2.Close()
|
|
||||||
if window.WaitKey(10) >= 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleFaceDetection(inputFile, xmlFile string) {
|
|
||||||
w, h := getVideoSize(inputFile)
|
|
||||||
log.Println(w, h)
|
|
||||||
|
|
||||||
pr1, pw1 := io.Pipe()
|
|
||||||
done1 := readProcess(inputFile, pw1)
|
|
||||||
openCvProcess(xmlFile, pr1, w, h)
|
|
||||||
err := <-done1
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
log.Println("Done")
|
|
||||||
}
|
|
197
examples/opencv_test.go
Normal file
197
examples/opencv_test.go
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
// +build gocv
|
||||||
|
|
||||||
|
// uncomment line above for gocv examples
|
||||||
|
|
||||||
|
package examples
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
ffmpeg "github.com/u2takey/ffmpeg-go"
|
||||||
|
"gocv.io/x/gocv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestExampleOpenCvFaceDetect will: take a video as input => use opencv for face detection => draw box and show a window
|
||||||
|
// This example depends on gocv and opencv, please refer: https://pkg.go.dev/gocv.io/x/gocv for installation.
|
||||||
|
func TestExampleOpenCvFaceDetectWithVideo(t *testing.T) {
|
||||||
|
inputFile := "./sample_data/head-pose-face-detection-male-short.mp4"
|
||||||
|
xmlFile := "./sample_data/haarcascade_frontalface_default.xml"
|
||||||
|
|
||||||
|
w, h := getVideoSize(inputFile)
|
||||||
|
log.Println(w, h)
|
||||||
|
|
||||||
|
pr1, pw1 := io.Pipe()
|
||||||
|
readProcess(inputFile, pw1)
|
||||||
|
openCvProcess(xmlFile, pr1, w, h)
|
||||||
|
log.Println("Done")
|
||||||
|
}
|
||||||
|
|
||||||
|
func readProcess(infileName string, writer io.WriteCloser) {
|
||||||
|
log.Println("Starting ffmpeg process1")
|
||||||
|
go func() {
|
||||||
|
err := ffmpeg.Input(infileName).
|
||||||
|
Output("pipe:",
|
||||||
|
ffmpeg.KwArgs{
|
||||||
|
"format": "rawvideo", "pix_fmt": "rgb24",
|
||||||
|
}).
|
||||||
|
WithOutput(writer).
|
||||||
|
ErrorToStdOut().
|
||||||
|
Run()
|
||||||
|
log.Println("ffmpeg process1 done")
|
||||||
|
_ = writer.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func openCvProcess(xmlFile string, reader io.ReadCloser, w, h int) {
|
||||||
|
// open display window
|
||||||
|
window := gocv.NewWindow("Face Detect")
|
||||||
|
defer window.Close()
|
||||||
|
|
||||||
|
// color for the rect when faces detected
|
||||||
|
blue := color.RGBA{B: 255}
|
||||||
|
|
||||||
|
classifier := gocv.NewCascadeClassifier()
|
||||||
|
defer classifier.Close()
|
||||||
|
|
||||||
|
if !classifier.Load(xmlFile) {
|
||||||
|
fmt.Printf("Error reading cascade file: %v\n", xmlFile)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
frameSize := w * h * 3
|
||||||
|
buf := make([]byte, frameSize, frameSize)
|
||||||
|
for {
|
||||||
|
n, err := io.ReadFull(reader, buf)
|
||||||
|
if n == 0 || err == io.EOF {
|
||||||
|
return
|
||||||
|
} else if n != frameSize || err != nil {
|
||||||
|
panic(fmt.Sprintf("read error: %d, %s", n, err))
|
||||||
|
}
|
||||||
|
img, err := gocv.NewMatFromBytes(h, w, gocv.MatTypeCV8UC3, buf)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("decode fail", err)
|
||||||
|
}
|
||||||
|
if img.Empty() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
img2 := gocv.NewMat()
|
||||||
|
gocv.CvtColor(img, &img2, gocv.ColorBGRToRGB)
|
||||||
|
|
||||||
|
// detect faces
|
||||||
|
rects := classifier.DetectMultiScale(img2)
|
||||||
|
fmt.Printf("found %d faces\n", len(rects))
|
||||||
|
|
||||||
|
// draw a rectangle around each face on the original image, along with text identifing as "Human"
|
||||||
|
for _, r := range rects {
|
||||||
|
gocv.Rectangle(&img2, r, blue, 3)
|
||||||
|
|
||||||
|
size := gocv.GetTextSize("Human", gocv.FontHersheyPlain, 1.2, 2)
|
||||||
|
pt := image.Pt(r.Min.X+(r.Min.X/2)-(size.X/2), r.Min.Y-2)
|
||||||
|
gocv.PutText(&img2, "Human", pt, gocv.FontHersheyPlain, 1.2, blue, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show the image in the window, and wait 1 millisecond
|
||||||
|
window.IMShow(img2)
|
||||||
|
img.Close()
|
||||||
|
img2.Close()
|
||||||
|
if window.WaitKey(10) >= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExampleOpenCvFaceDetectWithCamera will: task stream from webcam => use opencv for face detection => output with ffmpeg
|
||||||
|
// This example depends on gocv and opencv, please refer: https://pkg.go.dev/gocv.io/x/gocv for installation.
|
||||||
|
func TestExampleOpenCvFaceDetectWithCamera(t *testing.T) {
|
||||||
|
deviceID := "0" // camera device id
|
||||||
|
xmlFile := "./sample_data/haarcascade_frontalface_default.xml"
|
||||||
|
|
||||||
|
webcam, err := gocv.OpenVideoCapture(deviceID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("error opening video capture device: %v\n", deviceID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer webcam.Close()
|
||||||
|
|
||||||
|
// prepare image matrix
|
||||||
|
img := gocv.NewMat()
|
||||||
|
defer img.Close()
|
||||||
|
|
||||||
|
if ok := webcam.Read(&img); !ok {
|
||||||
|
panic(fmt.Sprintf("Cannot read device %v", deviceID))
|
||||||
|
}
|
||||||
|
fmt.Printf("img: %vX%v\n", img.Cols(), img.Rows())
|
||||||
|
|
||||||
|
pr1, pw1 := io.Pipe()
|
||||||
|
writeProcess("./sample_data/face_detect.mp4", pr1, img.Cols(), img.Rows())
|
||||||
|
|
||||||
|
// color for the rect when faces detected
|
||||||
|
blue := color.RGBA{B: 255}
|
||||||
|
|
||||||
|
// load classifier to recognize faces
|
||||||
|
classifier := gocv.NewCascadeClassifier()
|
||||||
|
defer classifier.Close()
|
||||||
|
|
||||||
|
if !classifier.Load(xmlFile) {
|
||||||
|
fmt.Printf("Error reading cascade file: %v\n", xmlFile)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Start reading device: %v\n", deviceID)
|
||||||
|
for i := 0; i < 200; i++ {
|
||||||
|
if ok := webcam.Read(&img); !ok {
|
||||||
|
fmt.Printf("Device closed: %v\n", deviceID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if img.Empty() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// detect faces
|
||||||
|
rects := classifier.DetectMultiScale(img)
|
||||||
|
fmt.Printf("found %d faces\n", len(rects))
|
||||||
|
|
||||||
|
// draw a rectangle around each face on the original image, along with text identifing as "Human"
|
||||||
|
for _, r := range rects {
|
||||||
|
gocv.Rectangle(&img, r, blue, 3)
|
||||||
|
|
||||||
|
size := gocv.GetTextSize("Human", gocv.FontHersheyPlain, 1.2, 2)
|
||||||
|
pt := image.Pt(r.Min.X+(r.Min.X/2)-(size.X/2), r.Min.Y-2)
|
||||||
|
gocv.PutText(&img, "Human", pt, gocv.FontHersheyPlain, 1.2, blue, 2)
|
||||||
|
}
|
||||||
|
pw1.Write(img.ToBytes())
|
||||||
|
}
|
||||||
|
pw1.Close()
|
||||||
|
log.Println("Done")
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeProcess(outputFile string, reader io.ReadCloser, w, h int) {
|
||||||
|
log.Println("Starting ffmpeg process1")
|
||||||
|
go func() {
|
||||||
|
err := ffmpeg.Input("pipe:",
|
||||||
|
ffmpeg.KwArgs{"format": "rawvideo",
|
||||||
|
"pix_fmt": "bgr24", "s": fmt.Sprintf("%dx%d", w, h),
|
||||||
|
}).
|
||||||
|
Overlay(ffmpeg.Input("./sample_data/overlay.png"), "").
|
||||||
|
Output(outputFile).
|
||||||
|
WithInput(reader).
|
||||||
|
ErrorToStdOut().
|
||||||
|
OverWriteOutput().
|
||||||
|
Run()
|
||||||
|
log.Println("ffmpeg process1 done")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
_ = reader.Close()
|
||||||
|
}()
|
||||||
|
}
|
Binary file not shown.
@@ -84,6 +84,31 @@ func TestSimpleOutputArgs(t *testing.T) {
|
|||||||
"2", "-vf", "fps=3", "imageFromVideo_%d.jpg"}, cmd.GetArgs())
|
"2", "-vf", "fps=3", "imageFromVideo_%d.jpg"}, cmd.GetArgs())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAutomaticStreamSelection(t *testing.T) {
|
||||||
|
// example from http://ffmpeg.org/ffmpeg-all.html
|
||||||
|
input := []*Stream{Input("A.avi"), Input("B.mp4")}
|
||||||
|
out1 := Output(input, "out1.mkv")
|
||||||
|
out2 := Output(input, "out2.wav")
|
||||||
|
out3 := Output(input, "out3.mov", KwArgs{"map": "1:a", "c:a": "copy"})
|
||||||
|
cmd := MergeOutputs(out1, out2, out3)
|
||||||
|
printArgs(cmd.GetArgs())
|
||||||
|
printGraph(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLabeledFiltergraph(t *testing.T) {
|
||||||
|
// example from http://ffmpeg.org/ffmpeg-all.html
|
||||||
|
in1, in2, in3 := Input("A.avi"), Input("B.mp4"), Input("C.mkv")
|
||||||
|
in2Split := in2.Get("v").Hue(KwArgs{"s": 0}).Split()
|
||||||
|
overlay := Filter([]*Stream{in1, in2}, "overlay", nil)
|
||||||
|
aresample := Filter([]*Stream{in1, in2, in3}, "aresample", nil)
|
||||||
|
out1 := Output([]*Stream{in2Split.Get("outv1"), overlay, aresample}, "out1.mp4", KwArgs{"an": ""})
|
||||||
|
out2 := Output([]*Stream{in1, in2, in3}, "out2.mkv")
|
||||||
|
out3 := in2Split.Get("outv2").Output("out3.mkv", KwArgs{"map": "1:a:0"})
|
||||||
|
cmd := MergeOutputs(out1, out2, out3)
|
||||||
|
printArgs(cmd.GetArgs())
|
||||||
|
printGraph(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
func ComplexFilterExample() *Stream {
|
func ComplexFilterExample() *Stream {
|
||||||
split := Input(TestInputFile1).VFlip().Split()
|
split := Input(TestInputFile1).VFlip().Split()
|
||||||
split0, split1 := split.Get("0"), split.Get("1")
|
split0, split1 := split.Get("0"), split.Get("1")
|
||||||
@@ -311,6 +336,19 @@ func TestView(t *testing.T) {
|
|||||||
t.Log(b)
|
t.Log(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printArgs(args []string) {
|
||||||
|
for _, a := range args {
|
||||||
|
fmt.Printf("%s ", a)
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
func printGraph(s *Stream) {
|
||||||
|
fmt.Println()
|
||||||
|
v, _ := s.View(ViewTypeFlowChart)
|
||||||
|
fmt.Println(v)
|
||||||
|
}
|
||||||
|
|
||||||
//func TestAvFoundation(t *testing.T) {
|
//func TestAvFoundation(t *testing.T) {
|
||||||
// out := Input("default:none", KwArgs{"f": "avfoundation", "framerate": "30"}).
|
// out := Input("default:none", KwArgs{"f": "avfoundation", "framerate": "30"}).
|
||||||
// Output("output.mp4", KwArgs{"format": "mp4"}).
|
// Output("output.mp4", KwArgs{"format": "mp4"}).
|
||||||
|
30
filters.go
30
filters.go
@@ -5,7 +5,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AssetType(hasType, expectType string, action string) {
|
func AssertType(hasType, expectType string, action string) {
|
||||||
if hasType != expectType {
|
if hasType != expectType {
|
||||||
panic(fmt.Sprintf("cannot %s on non-%s", action, expectType))
|
panic(fmt.Sprintf("cannot %s on non-%s", action, expectType))
|
||||||
}
|
}
|
||||||
@@ -20,32 +20,32 @@ func Filter(streamSpec []*Stream, filterName string, args Args, kwArgs ...KwArgs
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) Filter(filterName string, args Args, kwArgs ...KwArgs) *Stream {
|
func (s *Stream) Filter(filterName string, args Args, kwArgs ...KwArgs) *Stream {
|
||||||
AssetType(s.Type, "FilterableStream", "filter")
|
AssertType(s.Type, "FilterableStream", "filter")
|
||||||
return Filter([]*Stream{s}, filterName, args, MergeKwArgs(kwArgs))
|
return Filter([]*Stream{s}, filterName, args, MergeKwArgs(kwArgs))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) Split() *Node {
|
func (s *Stream) Split() *Node {
|
||||||
AssetType(s.Type, "FilterableStream", "split")
|
AssertType(s.Type, "FilterableStream", "split")
|
||||||
return NewFilterNode("split", []*Stream{s}, 1, nil, nil)
|
return NewFilterNode("split", []*Stream{s}, 1, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) ASplit() *Node {
|
func (s *Stream) ASplit() *Node {
|
||||||
AssetType(s.Type, "FilterableStream", "asplit")
|
AssertType(s.Type, "FilterableStream", "asplit")
|
||||||
return NewFilterNode("asplit", []*Stream{s}, 1, nil, nil)
|
return NewFilterNode("asplit", []*Stream{s}, 1, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) SetPts(expr string) *Node {
|
func (s *Stream) SetPts(expr string) *Node {
|
||||||
AssetType(s.Type, "FilterableStream", "setpts")
|
AssertType(s.Type, "FilterableStream", "setpts")
|
||||||
return NewFilterNode("setpts", []*Stream{s}, 1, []string{expr}, nil)
|
return NewFilterNode("setpts", []*Stream{s}, 1, []string{expr}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) Trim(kwargs ...KwArgs) *Stream {
|
func (s *Stream) Trim(kwargs ...KwArgs) *Stream {
|
||||||
AssetType(s.Type, "FilterableStream", "trim")
|
AssertType(s.Type, "FilterableStream", "trim")
|
||||||
return NewFilterNode("trim", []*Stream{s}, 1, nil, MergeKwArgs(kwargs)).Stream("", "")
|
return NewFilterNode("trim", []*Stream{s}, 1, nil, MergeKwArgs(kwargs)).Stream("", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) Overlay(overlayParentNode *Stream, eofAction string, kwargs ...KwArgs) *Stream {
|
func (s *Stream) Overlay(overlayParentNode *Stream, eofAction string, kwargs ...KwArgs) *Stream {
|
||||||
AssetType(s.Type, "FilterableStream", "overlay")
|
AssertType(s.Type, "FilterableStream", "overlay")
|
||||||
if eofAction == "" {
|
if eofAction == "" {
|
||||||
eofAction = "repeat"
|
eofAction = "repeat"
|
||||||
}
|
}
|
||||||
@@ -55,24 +55,24 @@ func (s *Stream) Overlay(overlayParentNode *Stream, eofAction string, kwargs ...
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) HFlip(kwargs ...KwArgs) *Stream {
|
func (s *Stream) HFlip(kwargs ...KwArgs) *Stream {
|
||||||
AssetType(s.Type, "FilterableStream", "hflip")
|
AssertType(s.Type, "FilterableStream", "hflip")
|
||||||
return NewFilterNode("hflip", []*Stream{s}, 1, nil, MergeKwArgs(kwargs)).Stream("", "")
|
return NewFilterNode("hflip", []*Stream{s}, 1, nil, MergeKwArgs(kwargs)).Stream("", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) VFlip(kwargs ...KwArgs) *Stream {
|
func (s *Stream) VFlip(kwargs ...KwArgs) *Stream {
|
||||||
AssetType(s.Type, "FilterableStream", "vflip")
|
AssertType(s.Type, "FilterableStream", "vflip")
|
||||||
return NewFilterNode("vflip", []*Stream{s}, 1, nil, MergeKwArgs(kwargs)).Stream("", "")
|
return NewFilterNode("vflip", []*Stream{s}, 1, nil, MergeKwArgs(kwargs)).Stream("", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) Crop(x, y, w, h int, kwargs ...KwArgs) *Stream {
|
func (s *Stream) Crop(x, y, w, h int, kwargs ...KwArgs) *Stream {
|
||||||
AssetType(s.Type, "FilterableStream", "crop")
|
AssertType(s.Type, "FilterableStream", "crop")
|
||||||
return NewFilterNode("crop", []*Stream{s}, 1, []string{
|
return NewFilterNode("crop", []*Stream{s}, 1, []string{
|
||||||
strconv.Itoa(w), strconv.Itoa(h), strconv.Itoa(x), strconv.Itoa(y),
|
strconv.Itoa(w), strconv.Itoa(h), strconv.Itoa(x), strconv.Itoa(y),
|
||||||
}, MergeKwArgs(kwargs)).Stream("", "")
|
}, MergeKwArgs(kwargs)).Stream("", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) DrawBox(x, y, w, h int, color string, thickness int, kwargs ...KwArgs) *Stream {
|
func (s *Stream) DrawBox(x, y, w, h int, color string, thickness int, kwargs ...KwArgs) *Stream {
|
||||||
AssetType(s.Type, "FilterableStream", "drawbox")
|
AssertType(s.Type, "FilterableStream", "drawbox")
|
||||||
args := MergeKwArgs(kwargs)
|
args := MergeKwArgs(kwargs)
|
||||||
if thickness != 0 {
|
if thickness != 0 {
|
||||||
args["t"] = thickness
|
args["t"] = thickness
|
||||||
@@ -83,7 +83,7 @@ func (s *Stream) DrawBox(x, y, w, h int, color string, thickness int, kwargs ...
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) Drawtext(text string, x, y int, escape bool, kwargs ...KwArgs) *Stream {
|
func (s *Stream) Drawtext(text string, x, y int, escape bool, kwargs ...KwArgs) *Stream {
|
||||||
AssetType(s.Type, "FilterableStream", "drawtext")
|
AssertType(s.Type, "FilterableStream", "drawtext")
|
||||||
args := MergeKwArgs(kwargs)
|
args := MergeKwArgs(kwargs)
|
||||||
if escape {
|
if escape {
|
||||||
text = fmt.Sprintf("%q", text)
|
text = fmt.Sprintf("%q", text)
|
||||||
@@ -119,17 +119,17 @@ func (s *Stream) Concat(streams []*Stream, kwargs ...KwArgs) *Stream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) ZoomPan(kwargs ...KwArgs) *Stream {
|
func (s *Stream) ZoomPan(kwargs ...KwArgs) *Stream {
|
||||||
AssetType(s.Type, "FilterableStream", "zoompan")
|
AssertType(s.Type, "FilterableStream", "zoompan")
|
||||||
return NewFilterNode("zoompan", []*Stream{s}, 1, nil, MergeKwArgs(kwargs)).Stream("", "")
|
return NewFilterNode("zoompan", []*Stream{s}, 1, nil, MergeKwArgs(kwargs)).Stream("", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) Hue(kwargs ...KwArgs) *Stream {
|
func (s *Stream) Hue(kwargs ...KwArgs) *Stream {
|
||||||
AssetType(s.Type, "FilterableStream", "hue")
|
AssertType(s.Type, "FilterableStream", "hue")
|
||||||
return NewFilterNode("hue", []*Stream{s}, 1, nil, MergeKwArgs(kwargs)).Stream("", "")
|
return NewFilterNode("hue", []*Stream{s}, 1, nil, MergeKwArgs(kwargs)).Stream("", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo fix this
|
// todo fix this
|
||||||
func (s *Stream) ColorChannelMixer(kwargs ...KwArgs) *Stream {
|
func (s *Stream) ColorChannelMixer(kwargs ...KwArgs) *Stream {
|
||||||
AssetType(s.Type, "FilterableStream", "colorchannelmixer")
|
AssertType(s.Type, "FilterableStream", "colorchannelmixer")
|
||||||
return NewFilterNode("colorchannelmixer", []*Stream{s}, 1, nil, MergeKwArgs(kwargs)).Stream("", "")
|
return NewFilterNode("colorchannelmixer", []*Stream{s}, 1, nil, MergeKwArgs(kwargs)).Stream("", "")
|
||||||
}
|
}
|
||||||
|
1
view.go
1
view.go
@@ -81,6 +81,7 @@ func visualizeForMermaidAsFlowChart(s *Stream) (string, error) {
|
|||||||
next := outGoingMap[node.Hash()]
|
next := outGoingMap[node.Hash()]
|
||||||
for k, v := range next {
|
for k, v := range next {
|
||||||
for _, nextNode := range v {
|
for _, nextNode := range v {
|
||||||
|
// todo ignore merged output
|
||||||
label := string(k)
|
label := string(k)
|
||||||
if label == "" {
|
if label == "" {
|
||||||
label = "<>"
|
label = "<>"
|
||||||
|
Reference in New Issue
Block a user