You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用GoCV录制MJPEG格式1920x1080视频时避免快进(Timelapse)问题的技术问询

GoCV录制MJPEG视频设置分辨率后出现快进(Timelapse)问题

问题现象

我用GoCV捕获并保存MJPEG格式视频,不指定分辨率时录制功能正常,能以30fps帧率录制并播放。但一旦修改录制图像尺寸,就出现了快进问题:摄像头实际录制时长比预期长很多,所有内容被压缩到设定的视频时长里。比如按30fps录制120帧视频,预期录制和最终视频时长都应为4秒,但实际录制耗时约20秒,这20秒的内容被压缩到4秒视频中,导致快进效果。

我的疑问

我想知道如何避免这种情况?是不是我设置分辨率的方式有误?

已尝试的方法

  • 使用webcam.Set(3, 1920)这类命令设置分辨率
  • 尝试过gocv.NewMat()或NewMatWithSize(...)方法
    但以上尝试都没能解决问题。

我的代码

/*-------------------------------------------- savevideotest.go ----- | | Created - | | Version 1.0 2021-01-03 Initial version |------------------------------------------------------------------ | Version - - - |------------------------------------------------------------------ | Purpose: Video capture tool | Video Capture Tool to be used as main tool when it comes | to recording videos with the raspberry pi *-------------------------------------------------------------------*/
package main

import (
	"flag"
	"fmt"
	"gocv.io/x/gocv"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/op/go-logging"
	"os"
)

var(
	videolength = 120 //videolength in frames (recording with 25fps). 15k = 10min
	framecheck [2]int
	videoformat *string
	location *string
	camcount *int
	path *string
	log = logging.MustGetLogger("example")
	format = logging.MustStringFormatter(`%{color} %{time:2006:Jan:2:15:04:05.000} %{shortfunc} > %{level:.4s} %{id:03x}%{color:reset} %{message}`)
	// Mon Jan 2 15:04:05 MST 2006
)

/*-------------------------------------------- init ----- | Function init | Created 01.03.2021 - | Last change -- / ---- - -- | Purpose: Initialize flags for usage in other functions by parsing | Parameters: --- | Returns: --- *-------------------------------------------------------------------*/
func init() {
	videoformat = flag.String("videoformat",".avi","desired video format")
	location = flag.String("location", "BU_", "location of raspberry pi")
	camcount = flag.Int("camcount",1,"number of used cams")
	path = flag.String("path","","sets the path of videofile")
}

/*-------------------------------------------- main ----- | Function main | Created 01.03.2021 - | Last change -- / ---- - -- | Purpose: main function, creating infinite recording loop | with error control(var framecheck) and log generation | Parameters: --- | Returns: --- *-------------------------------------------------------------------*/
func main() {
	flag.Parse()
	printHeader()
	for {
		savevideo(videolength)
		//fmt.Printf("%v\n",framecheck)
		for k:= 0; k < *camcount; k++ {
			if framecheck[k] != videolength {
				fmt.Printf("Webcam %v hat nicht die geforderte Videolänge aufgenommen! Das Programm wird terminiert.", k)
				writeVideoLog(k)
				writeErrorLog(k)
				return
			}else {
				//framecheck[k] = 0
				writeVideoLog(k)
			}
		}
	}
}

/*-------------------------------------------- generateFileName ----- | Function AddItem | Created 01.03.2021 - | Last change -- / ---- - -- | Purpose: Function to generate the desired Filename by using flags | Parameters: deviceID int | Returns: filename.String *-------------------------------------------------------------------*/
func generateFileName(j int) string {
	flag.Parse()
	var filename strings.Builder
	filename.WriteString(*path)
	filename.WriteString(*location)
	filename.WriteString("__")
	filename.WriteString("Cam")
	filename.WriteString(strconv.Itoa(j))
	filename.WriteString("__")
	filename.WriteString(time.Now().Format("20060102_150405"))
	filename.WriteString(*videoformat)
	return filename.String()
}

/*-------------------------------------------- saveVideo ----- | Function saveVideo | Created 01.03.2021 - | Last change -- / ---- - -- | Purpose: Function to record and save the video | Parameters: videolength int | Returns: --- *-------------------------------------------------------------------*/
func savevideo(videolength int) {
	flag.Parse()
	var wg sync.WaitGroup
	wg.Add(*camcount)
	for j := 0; j < *camcount; j++ {
		go func(j int) {
			defer wg.Done()
			deviceID := j
			saveFile := generateFileName(j)

			webcam, err := gocv.VideoCaptureDevice(deviceID)// OpenVideoCapture(deviceID)
			if err != nil {
				fmt.Printf("Error opening video capture device: %v\n", deviceID)
				return
			}
			defer webcam.Close()

			img := gocv.NewMatWithSize(1280, 720, gocv.MatTypeCV64F)
			defer img.Close()

			webcam.Set(3, 1280)
			webcam.Set(4, 780)

			if ok := webcam.Read(&img); !ok {
				fmt.Printf("Cannot read device %v\n", deviceID)
				return
			}

			writer, err := gocv.VideoWriterFile(saveFile, "MJPG", 30, 1280, 720, true)
			if err != nil {
				fmt.Printf("error opening video writer device: %v\n", saveFile)
				return
			}
			defer writer.Close()

			fmt.Printf("%v %v ", img.Cols(), img.Rows())

			for i := 0; i < videolength; i++ {
				if ok := webcam.Read(&img); !ok {
					fmt.Printf("Device closed: %v\n", deviceID)
					return
				}
				if img.Empty() {
					continue
				}
				framecheck[j]++
				writer.Write(img)
			}
		}(j)
	}
	wg.Wait()
}

/*-------------------------------------------- printHeader ----- | Function printHeader | Created 01.03.2021 - | Last change -- / ---- - -- | Purpose: Function to print the header of the logfile | Parameters: --- | Returns: --- *-------------------------------------------------------------------*/
func printHeader() {
	flag.Parse()
	file, err := os.OpenFile("logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	backend1 := logging.NewLogBackend(file, "", 0)
	backend2 := logging.NewLogBackend(file, "", 0)

	// For messages written to backend2 we want to add some additional
	// information to the output, including the used log level and the name of
	// the function.
	backend2Formatter := logging.NewBackendFormatter(backend2, format)

	// Only errors and more severe messages should be sent to backend1
	backend1Leveled := logging.AddModuleLevel(backend1)
	backend1Leveled.SetLevel(logging.ERROR, "")

	// Set the backends to be used.
	logging.SetBackend(backend1Leveled, backend2Formatter)

	log.Info("|---------------|--------|---------------------------\n")
	log.Info("|BUSA VIDEO LOG | |\n")
	log.Info("|---------------|--------|---------------------------\n")
	log.Info("|LOG SETUP | INFO | ROTATIONSIZE 800000000 [KBytes]]\n")
	log.Info("| | | Maximum size of Video directory\n")
	log.Info("| | | before old videos get deleted\n")
	log.Info("| |--------|---------------------------\n")
	log.Info("| | INFO | MaxFileSizeLOG 500 [KBytes]\n")
	log.Info("| | | Maximum size of Logfile\n")
	log.Info("| | | before old logfiles get copied\n")
	log.Info("| | | away to BUSA_VIDEO.log.TIMESTAMP\n")
	log.Info("| |--------|---------------------------\n")
	log.Info("| | INFO | MaxNumOldLOGS 10\n")
	log.Info("| | | Maximum number of old Logfiles\n")
	log.Info("| | | before oldest logfile get deleted\n")
	log.Info("|---------------|--------|---------------------------\n")
	log.Info("|LOCATION SETUP | INFO | LOCATION_NAME " + *location + "\n")
	log.Info("| | | Name of Location\n")
	log.Info("| | | Used in filename\n")
	log.Info("| |--------|---------------------------\n")
	log.Info("| | INFO | OBJECT_ID 1500021BU027-542~~~~~\n")
	log.Info("| | | ID for MQTT Topic to identify Object\n")
	log.Info("| | | where video are recorded\n")
	log.Info("|---------------|--------|---------------------------\n")
	log.Info("|MQTT SETUP | INFO | localIP 10.170.63.11\n")
	log.Info("| | | IP Addr that get used\n")
	log.Info("| | | in MQTT message for URL of Video\n")
	log.Info("| |--------|---------------------------\n")
	log.Info("| | INFO | BUSA_MQTT_BROKER\n")
	log.Info("| | | MQTT Broker\n")
	log.Info("| |--------|---------------------------\n")
	log.Info("| | INFO | BOX_ID 70820e1224c5\n")
	log.Info("| | | Hardware ID derived from MAC-Addr\n")
	log.Info("|---------------|--------|---------------------------\n")
}

/*-------------------------------------------- writeErrorLog ----- | Function writeErrorLog | Created 01.03.2021 - | Last change -- / ---- - -- | Purpose: Function to print an error log entry in case something wents wrong | Parameters: deviceID | Returns: --- *-------------------------------------------------------------------*/
func writeErrorLog(k int) {
	flag.Parse()
	file, err := os.OpenFile("logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	backend1 := logging.NewLogBackend(file, "", 0)
	backend2 := logging.NewLogBackend(file, "", 0)

	// For messages written to backend2 we want to add some additional
	// information to the output, including the used log level and the name of
	// the function.
	backend2Formatter := logging.NewBackendFormatter(backend2, format)

	// Only errors and more severe messages should be sent to backend1
	backend1Leveled := logging.AddModuleLevel(backend1)
	backend1Leveled.SetLevel(logging.ERROR, "")

	// Set the backends to be used.
	logging.SetBackend(backend1Leveled, backend2Formatter)

	log.Error(" | ERROR | Kritischer Fehler: Webcam " + strconv.Itoa(k) + " hat nicht die erforderliche Anzahl an Frames aufgenommen. Die Anwendung wurde beendet.\n")
}

/*-------------------------------------------- writeVideoLog ----- | Function writeVideoLog | Created 01.03.2021 - | Last change -- / ---- - -- | Purpose: Function to write an info log entry when a video is saved | Parameters: deviceID | Returns: --- *-------------------------------------------------------------------*/
func writeVideoLog(k int) {
	flag.Parse()
	file, err := os.OpenFile("logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	backend1 := logging.NewLogBackend(file, "", 0)
	backend2 := logging.NewLogBackend(file, "", 0)

	// For messages written to backend2 we want to add some additional
	// information to the output, including the used log level and the name of
	// the function.
	backend2Formatter := logging.NewBackendFormatter(backend2, format)

	// Only errors and more severe messages should be sent to backend1
	backend1Leveled := logging.AddModuleLevel(backend1)
	backend1Leveled.SetLevel(logging.ERROR, "")

	// Set the backends to be used.
	logging.SetBackend(backend1Leveled, backend2Formatter)

	log.Info(" | INFO | Neues Video " + generateFileName(k) + "\n")
}

个人背景

我是编程新手,刚学了一个月Java,现在作为实习生参与Go项目,正在学习Go语言和GoCV相关内容。另外特别希望deadprogram能看到这个问题,他在Go语言和GoCV领域的解答都非常专业,我之前查相关问题时经常看到他的回复。


内容的提问来源于stack exchange,提问作者bilbo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 10:47:32