131 lines
5.0 KiB
C#
131 lines
5.0 KiB
C#
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("处理完成!");
|
|
}
|
|
/// <summary>
|
|
/// 视频切片
|
|
/// </summary>
|
|
/// <param name="inputPath">输入</param>
|
|
/// <param name="outputDir">输出</param>
|
|
/// <param name="intervalSec">间隔多少秒</param>
|
|
/// <param name="ProcessorCount">线程数</param>
|
|
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");
|
|
}
|
|
/// <summary>
|
|
/// 差异比对
|
|
/// </summary>
|
|
/// <param name="frameDir"></param>
|
|
/// <param name="threshold"></param>
|
|
/// <param name="ProcessorCount"></param>
|
|
static void DetectChanges(string frameDir, double threshold,int ProcessorCount = 6)
|
|
{
|
|
var frameFiles = Directory.GetFiles(frameDir, "*.jpg")
|
|
.OrderBy(f => f)
|
|
.ToList();
|
|
|
|
Image<Rgb24> prevFrame = null;
|
|
string outputDir = "output";
|
|
Directory.CreateDirectory(outputDir);
|
|
var options = new ParallelOptions { MaxDegreeOfParallelism = ProcessorCount };
|
|
foreach (var frameFile in frameFiles)
|
|
{
|
|
using (var currFrame = Image.Load<Rgb24>(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 =>
|
|
//{
|
|
|
|
//});
|
|
}
|
|
/// <summary>
|
|
/// 计算帧差异
|
|
/// </summary>
|
|
/// <param name="img1"></param>
|
|
/// <param name="img2"></param>
|
|
/// <returns></returns>
|
|
static double CalculateFrameDifference(Image<Rgb24> img1, Image<Rgb24> 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]);
|
|
}
|
|
}
|
|
} |