我正在构建一个从视频制作屏幕截图的程序。 它从视频中提取帧(使用ffmpeg),然后将它们组合成一个文件。
一切正常,但有时我得到(几乎)黑色图像,大部分是在视频的开头和结尾。
我能想到的一个可能的解决方案是检测提取的帧是否暗。如果天黑,则从稍微不同的时间提取另一帧。
如何检测提取的帧是暗/黑?还是有其他方法可以解决这个问题吗?
private void getScreenshots_Click(object sender, EventArgs e)
{
int index = 0;
foreach (string value in this.filesList.Items)
{
string file = selectedFiles[index] + "\\" + value;
// ------------------------------------------------
// MediaInfo
// ------------------------------------------------
// https://github.com/Nicholi/MediaInfoDotNet
//
// get file width, height, and frame count
//
// get aspect ratio of the video
// and calculate height of thumbnail
// using width and aspect ratio
//
MediaInfo MI = new MediaInfo();
MI.Open(file);
var width = MI.Get(StreamKind.Video, 0, "Width");
var height = MI.Get(StreamKind.Video, 0, "Height");
decimal d = Decimal.Parse(MI.Get(StreamKind.Video, 0, "Duration"));
decimal frameCount = Decimal.Parse(MI.Get(StreamKind.Video, 0, "FrameCount"));
MI.Close();
decimal ratio = Decimal.Divide(Decimal.Parse(width), Decimal.Parse(height));
int newHeight = Decimal.ToInt32(Decimal.Divide(newWidth, ratio));
decimal startTime = Decimal.Divide(d, totalImages);
//totalImages - number of thumbnails the final image will have
for (int x = 0; x < totalImages; x++)
{
// increase the time where the thumbnail is taken on each iteration
decimal newTime = Decimal.Multiply(startTime, x);
string time = TimeSpan.FromMilliseconds(double.Parse(newTime.ToString())).ToString(@"hh\:mm\:ss");
string outputFile = this.tmpPath + "img-" + index + x + ".jpg";
// create individual thumbnails with ffmpeg
proc = new Process();
proc.StartInfo.FileName = "ffmpeg.exe";
proc.StartInfo.Arguments = "-y -seek_timestamp 1 -ss " + time + " -i \"" + file + "\" -frames:v 1 -qscale:v 3 \"" + outputFile + "\"";
proc.Start();
proc.WaitForExit();
}
// set width and height of final image
int w = (this.cols * newWidth) + (this.spacing * this.cols + this.spacing);
int h = (this.rows * newHeight) + (this.spacing * this.rows + this.spacing);
int left, top, i = 0;
// combine individual thumbnails into one image
using (Bitmap bmp = new Bitmap(w, h))
{
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(this.backgroundColor);
// this.rows - number of rows
for (int y = 0; y < this.rows; y++)
{
// put images on a column
// this.cols - number of columns
// when x = number of columns go to next row
for (int x = 0; x < this.cols; x++)
{
Image imgFromFile = Image.FromFile(this.tmpPath + "img-" + index + i + ".jpg");
MemoryStream imgFromStream = new MemoryStream();
imgFromFile.Save(imgFromStream, imgFromFile.RawFormat);
imgFromFile.Dispose();
left = (x * newWidth) + ((x + 1) * this.spacing);
top = (this.spacing * (y + 1)) + (newHeight * y);
g.DrawImage(Image.FromStream(imgFromStream), left, top, newWidth, newHeight);
i++;
}
}
}
// save the final image
bmp.Save(selectedFiles[index] + "\\" + value + ".jpg");
}
index++;
}
}
答案 0 :(得分:0)
您需要分析各个帧。循环遍历每个帧/图像的像素并计算整体亮度。
这实际上是在SO post中概述的,用于确定图像的整体亮度。您可以根据自己的需要应用相同的想法。
根据您希望如何实现这一点,可能会相当繁重。如果需要即时完成,您很可能希望将帧分析缩小到较小的部分而不是整个图像。但是,如果您将此作为更多的后处理技术,那么它可能无关紧要。
答案 1 :(得分:0)
感谢gmiley我设法解决了我的问题(希望如此)。
以下是:
我将创建缩略图的代码移动到了自己的函数
private void ffmpegGetThumb(string input, string output, double time, int getNewFrame = 0)
{
// create individual thumbnails with ffmpeg
string timeString = TimeSpan.FromMilliseconds(time).ToString(@"hh\:mm\:ss");
proc = new Process();
proc.StartInfo.FileName = "ffmpeg.exe";
proc.StartInfo.Arguments = "-y -seek_timestamp 1 -ss " + timeString + " -i \"" + input + "\" -frames:v 1 -qscale:v 3 \"" + output + "\"";
proc.Start();
proc.WaitForExit();
}
我创建了一个功能,可以将缩略图转换为位图,检查其亮度并调整缩略图的拍摄时间(如果需要)
private void brightThumb(string input, string output, double time, int getNewFrame, bool goBack)
{
//get initial thumbnail
this.ffmpegGetThumb(input, output, time);
Bitmap frame = this.ConvertToBitmap(output);
double brightness = CalculateAverageLightness(frame);
double newTime = time;
if (CalculateAverageLightness(frame) < 0.1)
{
// brightness is to low
// get a new thumbnail by increasing the time at which it's taken
if (getNewFrame == 1 && goBack == false)
{
newTime = time + 1000;
}
// if goBack is true (the time is beyond 75% of the total duration)
// we decrease the time (if curent time is at credits no point going forward)
else if (getNewFrame == 1 && goBack == true)
{
newTime = time - 1000;
}
// take a new thumbnail at the adujted time
this.brightThumb(input, output, newTime, 1, goBack);
}
}
调用函数,其中用于创建拇指的代码位于getScreenshots_Click()
bool goBack = false;
if (x > (totalImages/4) * 3)
{
goBack = true;
}
this.brightThumb(file, outputFile, time, 0, goBack);
性能方面我认为与执行此检查之前相比,执行时间没有大的变化。如果它发现很多黑暗的图像,那么它将重新拍摄它们,这显然会增加执行时间。
我能想到一个问题。如果视频以黑暗场景结束,其长度足以包含2个缩略图。在这种情况下,缩略图将是相同的,因为它将返回一秒,直到它找到足够明亮的图像。