Compare commits

..

No commits in common. "c6755cb58586e6294224df2f9a23df1d0379e086" and "8ba0319c6e5e38efdfbd313a45bf629642d3824c" have entirely different histories.

4 changed files with 48 additions and 61 deletions

View file

@ -2,6 +2,6 @@
name = "cz_conventional_commits"
tag_format = "$version"
version_scheme = "semver2"
version = "0.3.0"
version = "0.2.0"
update_changelog_on_bump = true
major_version_zero = true

1
.gitignore vendored
View file

@ -1,3 +1,2 @@
fps-go-brr
fps-go-brr-compact
.vscode/settings.json

View file

@ -1,12 +1,3 @@
## 0.3.0 (2025-06-15)
### Refactor
- change variable and function names to fit with golang conventions
- remove pointless else statments
- add package string
- **gitignore**: ignore compact build binary
## 0.2.0 (2025-06-14)
### Feat

97
main.go
View file

@ -1,4 +1,3 @@
// A Go CLI tool for video frame analysis and comparison. Analyze frame persistence, detect dropped frames, and export data for visualization tools like those used by Digital Foundry.
package main
import (
@ -23,7 +22,7 @@ func main() {
Usage: "Count frames",
Action: func(ctx context.Context, cmd *cli.Command) error {
return countVideoFrames(cmd.Args().First())
return count_video_frames(cmd.Args().First())
},
},
{
@ -40,12 +39,12 @@ func main() {
Action: func(ctx context.Context, cmd *cli.Command) error {
firstFrame, _ := getImageFromFilePath(cmd.StringArg("frame1"))
secondFrame, _ := getImageFromFilePath(cmd.StringArg("frame2"))
first_frame, _ := getImageFromFilePath(cmd.StringArg("frame1"))
second_frame, _ := getImageFromFilePath(cmd.StringArg("frame2"))
firstRGBA := imageToRGBA(firstFrame)
secondRGBA := imageToRGBA(secondFrame)
return compareFrames(firstRGBA, secondRGBA)
first_rgba := imageToRGBA(first_frame)
second_rgba := imageToRGBA(second_frame)
return compare_frames(first_rgba, second_rgba)
},
},
{
@ -62,12 +61,12 @@ func main() {
Action: func(ctx context.Context, cmd *cli.Command) error {
firstFrame, _ := getImageFromFilePath(cmd.StringArg("frame1"))
secondFrame, _ := getImageFromFilePath(cmd.StringArg("frame2"))
first_frame, _ := getImageFromFilePath(cmd.StringArg("frame1"))
second_frame, _ := getImageFromFilePath(cmd.StringArg("frame2"))
firstRGBA := imageToRGBA(firstFrame)
secondRGBA := imageToRGBA(secondFrame)
return compareFramesAlt(firstRGBA, secondRGBA)
first_rgba := imageToRGBA(first_frame)
second_rgba := imageToRGBA(second_frame)
return compare_frames_alt(first_rgba, second_rgba)
},
},
{
@ -120,7 +119,7 @@ func main() {
}
}
// countVideoFrames
// count_video_frames
// Prints out the total ammount of frames within `video`
//
// Parameters:
@ -128,18 +127,18 @@ func main() {
//
// Returns:
// - error
func countVideoFrames(video string) error {
func count_video_frames(video string) error {
log.Default().Print("Trying to open video at: " + video)
videoFile, _ := vidio.NewVideo(video)
video_file, _ := vidio.NewVideo(video)
count := 0
for videoFile.Read() {
for video_file.Read() {
count++
}
log.Default().Println("Video total frames: " + strconv.Itoa(count))
return nil
}
func compareFrames(frame1 *image.RGBA, frame2 *image.RGBA) error {
func compare_frames(frame1 *image.RGBA, frame2 *image.RGBA) error {
accumError := int64(0)
for i := 0; i < len(frame1.Pix); i++ {
@ -151,7 +150,7 @@ func compareFrames(frame1 *image.RGBA, frame2 *image.RGBA) error {
return nil
}
func compareFramesAlt(frame1 *image.RGBA, frame2 *image.RGBA) error {
func compare_frames_alt(frame1 *image.RGBA, frame2 *image.RGBA) error {
// diff_frame := image.NewRGBA(frame1.Rect)
accumError := int64(0)
for i := 0; i < len(frame1.Pix); i++ {
@ -173,10 +172,9 @@ func isDiffUInt8(x, y uint8) bool {
sq := d * d
if sq > 0 {
return true
} else {
return false
}
return false
}
func isDiffUInt8WithTolerance(x, y uint8, tolerance uint64) bool {
@ -184,46 +182,45 @@ func isDiffUInt8WithTolerance(x, y uint8, tolerance uint64) bool {
sq := d * d
if sq > tolerance {
return true
} else {
return false
}
return false
}
func countUniqueVideoFrames(videoPath1 string, videoPath2 string, minDiff uint64, useSqDiff bool) error {
video1, _ := vidio.NewVideo(videoPath1)
video2, _ := vidio.NewVideo(videoPath2)
video1Frame := image.NewRGBA(image.Rect(0, 0, video1.Width(), video1.Height()))
video2Frame := image.NewRGBA(image.Rect(0, 0, video2.Width(), video2.Height()))
video1.SetFrameBuffer(video1Frame.Pix)
video2.SetFrameBuffer(video2Frame.Pix)
totalFrames := 0
uniqueFrames := 0
func countUniqueVideoFrames(video_path1 string, video_path2 string, min_diff uint64, use_sq_diff bool) error {
video1, _ := vidio.NewVideo(video_path1)
video2, _ := vidio.NewVideo(video_path2)
video1_frame := image.NewRGBA(image.Rect(0, 0, video1.Width(), video1.Height()))
video2_frame := image.NewRGBA(image.Rect(0, 0, video2.Width(), video2.Height()))
video1.SetFrameBuffer(video1_frame.Pix)
video2.SetFrameBuffer(video2_frame.Pix)
total_frames := 0
unique_frames := 0
for video1.Read() {
totalFrames++
total_frames++
video2.Read()
accumError := uint64(0)
for i := 0; i < len(video1Frame.Pix); i++ {
if useSqDiff {
if isDiffUInt8WithTolerance(video1Frame.Pix[i], video2Frame.Pix[i], minDiff) {
for i := 0; i < len(video1_frame.Pix); i++ {
if use_sq_diff {
if isDiffUInt8WithTolerance(video1_frame.Pix[i], video2_frame.Pix[i], min_diff) {
accumError++
}
} else {
if isDiffUInt8(video1Frame.Pix[i], video2Frame.Pix[i]) {
if isDiffUInt8(video1_frame.Pix[i], video2_frame.Pix[i]) {
accumError++
}
}
}
if minDiff <= accumError {
uniqueFrames++
log.Default().Println("[" + strconv.Itoa(totalFrames) + "]Unique frame")
if min_diff <= accumError {
unique_frames++
log.Default().Println("[" + strconv.Itoa(total_frames) + "]Unique frame")
} else {
log.Default().Println("[" + strconv.Itoa(totalFrames) + "]Non-unique frame")
log.Default().Println("[" + strconv.Itoa(total_frames) + "]Non-unique frame")
}
}
video1.Close()
video2.Close()
log.Default().Println(strconv.Itoa(uniqueFrames) + "/" + strconv.Itoa(totalFrames) + " are unique!")
log.Default().Println(strconv.Itoa(unique_frames) + "/" + strconv.Itoa(total_frames) + " are unique!")
return nil
}
@ -271,10 +268,10 @@ func analyzeFramePersistence(videoPath string, tolerance uint64, csvOutput strin
return fmt.Errorf("failed to create CSV file: %v", err)
}
defer csvFile.Close()
csvWriter = csv.NewWriter(csvFile)
defer csvWriter.Flush()
err = csvWriter.Write([]string{"frame", "average_fps", "frame_time", "unique_frame_count", "real_frame_time"})
if err != nil {
return fmt.Errorf("failed to write CSV header: %v", err)
@ -289,10 +286,10 @@ func analyzeFramePersistence(videoPath string, tolerance uint64, csvOutput strin
currentFrameTime float64
realFrameTime float64
}
var frameAnalysisData []FrameData
var uniqueFrameDurations []int // Duration of each unique frame
currentFrame := image.NewRGBA(image.Rect(0, 0, video.Width(), video.Height()))
previousFrame := image.NewRGBA(image.Rect(0, 0, video.Width(), video.Height()))
video.SetFrameBuffer(currentFrame.Pix)
@ -319,7 +316,7 @@ func analyzeFramePersistence(videoPath string, tolerance uint64, csvOutput strin
uniqueFramesInCurrentSecond = 1
totalUniqueFrames = 1
currentUniqueFrameDuration = 1
// Store data for first frame
currentTime := float64(frameNumber) / fps
effectiveFPS := float64(totalUniqueFrames) / currentTime
@ -359,7 +356,7 @@ func analyzeFramePersistence(videoPath string, tolerance uint64, csvOutput strin
uniqueFrameDurations[totalUniqueFrames-1] = currentUniqueFrameDuration
}
}
if consecutiveDuplicateCount > 1 {
persistenceMs := float64(consecutiveDuplicateCount+1) * frameTimeMs
framePersistenceDurations = append(framePersistenceDurations, persistenceMs)
@ -370,7 +367,7 @@ func analyzeFramePersistence(videoPath string, tolerance uint64, csvOutput strin
uniqueFramesInCurrentSecond++
totalUniqueFrames++
copy(previousFrame.Pix, currentFrame.Pix)
// Start tracking new unique frame
currentUniqueFrameDuration = 1
}