using System; using System.Collections.Generic; using System.IO; using System.Linq; using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using MediaToolkit; using MediaToolkit.Model; using MediaToolkit.Options; using VideoAnalysisCore.Common; using VideoAnalysisCore.AICore.FFMPGE; using Microsoft.Extensions.Options; using System.Diagnostics; namespace VideoChangeDetector { class Program { static void Main(string[] args) { var taskID = "665310769946693"; var inputFile = $"F:\\Learn.VideoAnalysis\\testC\\bin\\Debug\\net8.0\\video\\{taskID}.mp4"; //inputFile = "F:\\Learn.VideoAnalysis\\testC\\bin\\Debug\\net8.0\\video\\ppt.mp4"; string tempFrameDir = "temp_frames"; var intervalSec = 5; double threshold = 15.0; Stopwatch sw = new Stopwatch(); sw.Start(); ExtractVideoFrames(inputFile, tempFrameDir, intervalSec); sw.Stop(); Console.WriteLine("视频切帧总共花费{0}ms.", sw.Elapsed.TotalMilliseconds); Stopwatch sw1 = new Stopwatch(); sw1.Start(); DetectChanges(tempFrameDir, threshold); sw1.Stop(); Console.WriteLine("视频分析帧总共花费{0}ms.", sw1.Elapsed.TotalMilliseconds); Console.WriteLine("处理完成!"); } /// /// 视频切片 /// /// 输入 /// 输出 /// 间隔多少秒 /// 线程数 static void ExtractVideoFrames(string inputPath, string outputDir, int intervalSec, int ProcessorCount = 6) { Directory.CreateDirectory(outputDir); var inputFile = new MediaFile { Filename = inputPath }; using var engine = new Engine(FFMPGEHandle.FFmpegPath); engine.CustomCommand($"-i {inputPath} -vf \"fps=1/{intervalSec},scale=320:180\" {outputDir}/frame_%03d.jpg"); } /// /// 差异比对 /// /// /// /// static void DetectChanges(string frameDir, double threshold,int ProcessorCount = 6) { var frameFiles = Directory.GetFiles(frameDir, "*.jpg") .OrderBy(f => f) .ToList(); Image prevFrame = null; string outputDir = "output"; Directory.CreateDirectory(outputDir); var options = new ParallelOptions { MaxDegreeOfParallelism = ProcessorCount }; foreach (var frameFile in frameFiles) { using (var currFrame = Image.Load(frameFile)) { if (prevFrame != null) { double diff = CalculateFrameDifference(prevFrame, currFrame); double timestamp = GetTimestampFromFileName(frameFile) * 5 ; if (diff > threshold) { string outputPath = Path.Combine(outputDir, $"change_{timestamp:0000}.jpg"); currFrame.Save(outputPath); Console.WriteLine($"变化帧: {timestamp}秒,差异值: {diff:F2}"); } else { Console.WriteLine($"-------: {timestamp}秒,差异值: {diff:F2}"); } } prevFrame?.Dispose(); prevFrame = currFrame.Clone(); } } //Parallel.ForEach(frameFiles, options, frameFile => //{ //}); } /// /// 计算帧差异 /// /// /// /// static double CalculateFrameDifference(Image img1, Image img2) { // 统一调整为64x64 var resized1 = img1.Clone(x => x.Resize(96, 96).Grayscale()); var resized2 = img2.Clone(x => x.Resize(96, 96).Grayscale()); long diff = 0; for (int y = 0; y < resized1.Height; y++) { for (int x = 0; x < resized1.Width; x++) { var pixel1 = resized1[x, y]; var pixel2 = resized2[x, y]; diff += Math.Abs(pixel1.R - pixel2.R); } } return diff / (double)(resized1.Width * resized1.Height); } static double GetTimestampFromFileName(string filePath) { string fileName = Path.GetFileNameWithoutExtension(filePath); return double.Parse(fileName.Split('_')[1]); } } }