繁体   English   中英

C#在图像中获得显色

[英]C# get dominant color in an image

我正在构建一个程序,可以从视频截屏。 它从视频中提取帧(使用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++;
    }
}

您将需要分析各个框架。 遍历每帧/图像的像素并计算总体亮度。

这实际上是在有关确定图像整体亮度的SO帖子中概述的。 您可以将相同的想法应用于您的需求。

根据您要实现此目标的方式,这可能会很繁重。 如果需要即时执行此操作,则您很可能希望将帧分析的范围缩小到较小的部分,而不是整个图像。 但是,如果您将此操作更多地视为一种后处理技术,则可能没有太大关系。

多亏了gmiley,我才得以解决我的问题(希望如此)。

这是如何做:

  1. 我在课堂上加入了由gmiley链接函数
  2. 我添加了一个获取图像并将其转换为位图的函数,可在此处找到
  3. 我将创建缩略图的代码移至其自己的功能

     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(); } 
  4. 我创建了一个函数,可将缩略图转换为位图,检查其亮度并根据需要调整拍摄缩略图的时间。

     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); } } 
  5. 调用该函数,用于创建拇指的代码位于getScreenshots_Click()中

     bool goBack = false; if (x > (totalImages/4) * 3) { goBack = true; } this.brightThumb(file, outputFile, time, 0, goBack); 

在性能方面,与执行此检查之前相比,我认为执行时间没有太大变化。 如果发现很多暗图像,它将重新拍摄它们,因此这显然会增加执行时间。

我可以想到一个问题。 如果视频以黑暗场景结束,时间足够长,可以包含2个缩略图。 在这种情况下,缩略图将是相同的,因为它将返回一秒钟,直到找到足够明亮的图像为止。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM