winforms 有没有一种方法可以使用ffmpeg从视频文件中提取帧到内存中,并对每个帧进行一些操作?

w6mmgewl  于 2022-11-16  发布在  其他
关注(0)|答案(1)|浏览(196)

目标是每次从视频文件中提取一帧,然后从图像中制作直方图,然后移动到下一帧。
帧提取和直方图处理是工作正常时,帧已保存为图像在硬盘上。但现在我想做这一切在内存中。
为了提取帧,我使用ffmpeg,因为我认为它足够快:

ffmpeg -r 1 -i MyVid.mp4 -r 1 "$filename%03d.png

现在我在命令提示符窗口中使用ffmpeg。
使用此命令,它将在硬盘上保存超过65000幅图像(帧)。但不是将它们保存在硬盘上,我想知道我是否可以对内存中的每一帧进行直方图操作,而不是将所有65000帧保存到硬盘上。
然后我想找到特定的图像使用直方图和保存到硬盘上这帧。
直方图部分现在也使用来自硬盘而不是来自存储器的文件:

private void btnLoadHistogram_Click(object sender, System.EventArgs e)
        {
            string[] files = Directory.GetFiles(@"d:\screenshots\", "*.jpg");

            for (int i = 0; i < files.Length; i++)
            {
                sbInfo.Text = "Loading image";
                if (pbImage.Image != null)
                    pbImage.Image.Dispose();

                pbImage.Image = Image.FromFile(files[i]);//txtFileName.Text);

                Application.DoEvents();

                sbInfo.Text = "Computing histogram";
                long[] myValues = GetHistogram(new Bitmap(pbImage.Image));

                Histogram.DrawHistogram(myValues);

                sbInfo.Text = "";   
            }   
        }

        public long[] GetHistogram(System.Drawing.Bitmap picture)
        {
            long[] myHistogram = new long[256];

            for (int i=0;i<picture.Size.Width;i++)
                for (int j=0;j<picture.Size.Height;j++)
                {
                    System.Drawing.Color c  = picture.GetPixel(i,j);

                    long Temp=0;
                    Temp+=c.R;
                    Temp+=c.G;
                    Temp+=c.B;

                    Temp = (int) Temp/3;
                    myHistogram[Temp]++;
                }

            return myHistogram;
        }

和控制对象HistogramaDesenat类的代码:

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace Histograma
{
    /// <summary>
    /// Summary description for HistogramaDesenat.
    /// </summary>
    public class HistogramaDesenat : System.Windows.Forms.UserControl
    {
        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;

        public HistogramaDesenat()
        {
            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();

            // TODO: Add any initialization after the InitializeComponent call

            this.Paint += new PaintEventHandler(HistogramaDesenat_Paint);
            this.Resize+=new EventHandler(HistogramaDesenat_Resize);
        }

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if(components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        #region Component Designer generated code
        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            // 
            // HistogramaDesenat
            // 
            this.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
            this.Name = "HistogramaDesenat";
            this.Size = new System.Drawing.Size(208, 176);
        }
        #endregion

        private void HistogramaDesenat_Paint(object sender, PaintEventArgs e)
        {
            if (myIsDrawing)
            {

                Graphics g = e.Graphics;
                Pen myPen = new Pen(new SolidBrush(myColor),myXUnit);
                //The width of the pen is given by the XUnit for the control.
                for (int i=0;i<myValues.Length;i++)
                {

                    //We draw each line 
                    g.DrawLine(myPen,
                        new PointF(myOffset + (i*myXUnit), this.Height - myOffset), 
                        new PointF(myOffset + (i*myXUnit), this.Height - myOffset - myValues[i] * myYUnit));

                    //We plot the coresponding index for the maximum value.
                    if (myValues[i]==myMaxValue)
                    {
                        SizeF mySize = g.MeasureString(i.ToString(),myFont);

                        g.DrawString(i.ToString(),myFont,new SolidBrush(myColor),
                            new PointF(myOffset + (i*myXUnit) - (mySize.Width/2), this.Height - myFont.Height ),
                            System.Drawing.StringFormat.GenericDefault);
                    }
                }

                //We draw the indexes for 0 and for the length of the array beeing plotted
                g.DrawString("0",myFont, new SolidBrush(myColor),new PointF(myOffset,this.Height - myFont.Height),System.Drawing.StringFormat.GenericDefault);
                g.DrawString((myValues.Length-1).ToString(),myFont, 
                    new SolidBrush(myColor),
                    new PointF(myOffset + (myValues.Length * myXUnit) - g.MeasureString((myValues.Length-1).ToString(),myFont).Width,
                    this.Height - myFont.Height),
                    System.Drawing.StringFormat.GenericDefault);

                //We draw a rectangle surrounding the control.
                g.DrawRectangle(new System.Drawing.Pen(new SolidBrush(Color.Black),1),0,0,this.Width-1,this.Height-1);
            }

        }

        long myMaxValue;
        private long[] myValues;
        private bool myIsDrawing;

        private float myYUnit; //this gives the vertical unit used to scale our values
        private float myXUnit; //this gives the horizontal unit used to scale our values
        private int myOffset = 20; //the offset, in pixels, from the control margins.

        private Color myColor = Color.Black;
        private Font myFont = new Font("Tahoma",10);

        [Category("Histogram Options")]
        [Description ("The distance from the margins for the histogram")]
        public int Offset
        {
            set
            {
                if (value>0)
                    myOffset= value;
            }
            get
            {
                return myOffset;
            }
        }

        [Category("Histogram Options")]
        [Description ("The color used within the control")]
        public Color DisplayColor
        {
            set
            {
                myColor = value;
            }
            get
            {
                return myColor;
            }
        }

        /// <summary>
        /// We draw the histogram on the control
        /// </summary>
        /// <param name="myValues">The values beeing draw</param>
        public void DrawHistogram(long[] Values)
        {
            myValues = new long[Values.Length];
            Values.CopyTo(myValues,0);

            myIsDrawing = true;
            myMaxValue = getMaxim(myValues);

            ComputeXYUnitValues();

            this.Refresh();
        }

        /// <summary>
        /// We get the highest value from the array
        /// </summary>
        /// <param name="Vals">The array of values in which we look</param>
        /// <returns>The maximum value</returns>
        private long getMaxim(long[] Vals)
        {
            if (myIsDrawing)
            {
                long max = 0;
                for (int i=0;i<Vals.Length;i++)
                {
                    if (Vals[i] > max)
                        max = Vals[i];
                }
                return max;
            }
            return 1;
        }

        private void HistogramaDesenat_Resize(object sender, EventArgs e)
        {
            if (myIsDrawing)
            {
                ComputeXYUnitValues();
            }
            this.Refresh();
        }

        private void ComputeXYUnitValues()
        {
            myYUnit = (float) (this.Height - (2 * myOffset)) / myMaxValue;
            myXUnit = (float) (this.Width - (2 * myOffset)) / (myValues.Length-1);
        }
    }
}

所以最后我想做的是:

  • 使用FFMPEG从存储器中的视频文件中提取帧。
  • 而不是使用Directory.GetFiles,我想对ffmpeg从内存中提取的每一帧进行直方图操作。
  • 每个提取的帧图像使用直方图来发现图像中是否存在 lightning (天气 lightning )。
  • 如果有 lightning ,则将帧图像保存到硬盘。
l7wslrjt

l7wslrjt1#

对于ffmpeg,请尝试FFmpeg.AutoGen
但是你需要学习ffmpeg api的解复用器和解码器,以获得原始帧。
对于opencv,请尝试emgucv(推荐)
您可以尝试在this之类的地方搜索示例

相关问题