Compare commits

...

5 commits

Author SHA1 Message Date
c6755cb585
bump: version 0.2.0 → 0.3.0
All checks were successful
Build and Release / build (push) Successful in 1m38s
2025-06-15 03:00:23 +10:00
aec0f14c68
refactor: change variable and function names to fit with golang conventions 2025-06-15 03:00:09 +10:00
3529f7e415
refactor: remove pointless else statments 2025-06-15 02:57:54 +10:00
f9575c92a9
refactor: add package string 2025-06-15 02:57:21 +10:00
1b4cd880ef
chore(gitignore): ignore .vscode settings 2025-06-15 02:54:45 +10:00
4 changed files with 61 additions and 48 deletions

View file

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

1
.gitignore vendored
View file

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

View file

@ -1,3 +1,12 @@
## 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,3 +1,4 @@
// 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 (
@ -22,7 +23,7 @@ func main() {
Usage: "Count frames",
Action: func(ctx context.Context, cmd *cli.Command) error {
return count_video_frames(cmd.Args().First())
return countVideoFrames(cmd.Args().First())
},
},
{
@ -39,12 +40,12 @@ func main() {
Action: func(ctx context.Context, cmd *cli.Command) error {
first_frame, _ := getImageFromFilePath(cmd.StringArg("frame1"))
second_frame, _ := getImageFromFilePath(cmd.StringArg("frame2"))
firstFrame, _ := getImageFromFilePath(cmd.StringArg("frame1"))
secondFrame, _ := getImageFromFilePath(cmd.StringArg("frame2"))
first_rgba := imageToRGBA(first_frame)
second_rgba := imageToRGBA(second_frame)
return compare_frames(first_rgba, second_rgba)
firstRGBA := imageToRGBA(firstFrame)
secondRGBA := imageToRGBA(secondFrame)
return compareFrames(firstRGBA, secondRGBA)
},
},
{
@ -61,12 +62,12 @@ func main() {
Action: func(ctx context.Context, cmd *cli.Command) error {
first_frame, _ := getImageFromFilePath(cmd.StringArg("frame1"))
second_frame, _ := getImageFromFilePath(cmd.StringArg("frame2"))
firstFrame, _ := getImageFromFilePath(cmd.StringArg("frame1"))
secondFrame, _ := getImageFromFilePath(cmd.StringArg("frame2"))
first_rgba := imageToRGBA(first_frame)
second_rgba := imageToRGBA(second_frame)
return compare_frames_alt(first_rgba, second_rgba)
firstRGBA := imageToRGBA(firstFrame)
secondRGBA := imageToRGBA(secondFrame)
return compareFramesAlt(firstRGBA, secondRGBA)
},
},
{
@ -119,7 +120,7 @@ func main() {
}
}
// count_video_frames
// countVideoFrames
// Prints out the total ammount of frames within `video`
//
// Parameters:
@ -127,18 +128,18 @@ func main() {
//
// Returns:
// - error
func count_video_frames(video string) error {
func countVideoFrames(video string) error {
log.Default().Print("Trying to open video at: " + video)
video_file, _ := vidio.NewVideo(video)
videoFile, _ := vidio.NewVideo(video)
count := 0
for video_file.Read() {
for videoFile.Read() {
count++
}
log.Default().Println("Video total frames: " + strconv.Itoa(count))
return nil
}
func compare_frames(frame1 *image.RGBA, frame2 *image.RGBA) error {
func compareFrames(frame1 *image.RGBA, frame2 *image.RGBA) error {
accumError := int64(0)
for i := 0; i < len(frame1.Pix); i++ {
@ -150,7 +151,7 @@ func compare_frames(frame1 *image.RGBA, frame2 *image.RGBA) error {
return nil
}
func compare_frames_alt(frame1 *image.RGBA, frame2 *image.RGBA) error {
func compareFramesAlt(frame1 *image.RGBA, frame2 *image.RGBA) error {
// diff_frame := image.NewRGBA(frame1.Rect)
accumError := int64(0)
for i := 0; i < len(frame1.Pix); i++ {
@ -172,9 +173,10 @@ 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 {
@ -182,45 +184,46 @@ func isDiffUInt8WithTolerance(x, y uint8, tolerance uint64) bool {
sq := d * d
if sq > tolerance {
return true
} else {
return false
}
return false
}
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
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
for video1.Read() {
total_frames++
totalFrames++
video2.Read()
accumError := uint64(0)
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) {
for i := 0; i < len(video1Frame.Pix); i++ {
if useSqDiff {
if isDiffUInt8WithTolerance(video1Frame.Pix[i], video2Frame.Pix[i], minDiff) {
accumError++
}
} else {
if isDiffUInt8(video1_frame.Pix[i], video2_frame.Pix[i]) {
if isDiffUInt8(video1Frame.Pix[i], video2Frame.Pix[i]) {
accumError++
}
}
}
if min_diff <= accumError {
unique_frames++
log.Default().Println("[" + strconv.Itoa(total_frames) + "]Unique frame")
if minDiff <= accumError {
uniqueFrames++
log.Default().Println("[" + strconv.Itoa(totalFrames) + "]Unique frame")
} else {
log.Default().Println("[" + strconv.Itoa(total_frames) + "]Non-unique frame")
log.Default().Println("[" + strconv.Itoa(totalFrames) + "]Non-unique frame")
}
}
video1.Close()
video2.Close()
log.Default().Println(strconv.Itoa(unique_frames) + "/" + strconv.Itoa(total_frames) + " are unique!")
log.Default().Println(strconv.Itoa(uniqueFrames) + "/" + strconv.Itoa(totalFrames) + " are unique!")
return nil
}
@ -268,10 +271,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)
@ -286,10 +289,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)
@ -316,7 +319,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
@ -356,7 +359,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)
@ -367,7 +370,7 @@ func analyzeFramePersistence(videoPath string, tolerance uint64, csvOutput strin
uniqueFramesInCurrentSecond++
totalUniqueFrames++
copy(previousFrame.Pix, currentFrame.Pix)
// Start tracking new unique frame
currentUniqueFrameDuration = 1
}