主代码
c
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TextDancing
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// 区域大小
int maskSize = 2;
// 字体
Font font = new Font("仿宋", 20);
// 字母c的间隔
int unit_w = 20;
//1.提取gif
private void btnGIF_Click(object sender, EventArgs e)
{
// 路径
string gifPath = "1.gif";
// 载入gif图片
using (Image gifImg = Image.FromFile(gifPath))
{
// 每一帧的维度(宽高)
FrameDimension dimension = new FrameDimension(gifImg.FrameDimensionsList[0]);
// 获取帧数
int frameCount = gifImg.GetFrameCount(dimension);
for (int i = 0; i < frameCount; i++)
{
// 选择当前帧
gifImg.SelectActiveFrame(dimension, i);
// 每一张图片
// frame 原始gif 的每一张图片
using (Bitmap frame = new Bitmap(gifImg))
{
// bmp:准备转换的图片
Bitmap bmp = new Bitmap(frame.Width * 10, frame.Height * 10);
Graphics g = Graphics.FromImage(bmp);
// 纯黑的背景
g.Clear(Color.Black);
// 抗锯齿
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
int idxI = 0;
int idxJ = 0;
for (int m = 0; m < frame.Width; m+=maskSize)
{
idxJ = 0;
for (int n = 0; n < frame.Height; n+=maskSize)
{
Color c = frame.GetPixel(m, n);
int gray = (c.R + c.G + c.B) / 3;
///
bool draw = true;
String s = "蔡";
if (draw == true)
{
g.DrawString("c", font,
new SolidBrush(Color.FromArgb(gray, gray, gray)),
idxI * unit_w, idxJ * unit_w);
}
idxJ++;
}
idxI++;
}
bmp.Save("cool//" + i.ToString() + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
}
}
//2.合成视频
private void btnAVI_Click(object sender, EventArgs e)
{
SynthesisVideo("processedFrameMP4", 24);
}
//3.提取视频帧
private void btnMP4_Click(object sender, EventArgs e)
{
ExtractFrames("2.mp4", "frameMP4");
}
// 合成视频
public void SynthesisVideo(string path, int frame_rate = 30)
{
AVIWriter aviWriter = new AVIWriter();
// 获取指定文件夹中所有的 .jpg 文件
string[] imageFiles = Directory.GetFiles(path, "*.jpg");
Bitmap bmp1 = new Bitmap(imageFiles[0]);
//:avi中所有图像皆不能小于width及height
Bitmap avi_frame = aviWriter.Create(System.DateTime.Now.Hour.ToString() + "_"
+ System.DateTime.Now.Minute.ToString() + "_" +
System.DateTime.Now.Second.ToString() + ".avi", (uint)frame_rate, bmp1.Width, bmp1.Height);
//由于转化为avi后呈现相反,所以翻转
for (int i = 0; i < imageFiles.Length; i++)
{
Bitmap bmpKun = new Bitmap(imageFiles[i]);
bmpKun.RotateFlip(RotateFlipType.Rotate180FlipX);
//载入图像
aviWriter.LoadFrame(bmpKun);
aviWriter.AddFrame();
}
//释放资源
aviWriter.Close();
avi_frame.Dispose();
MessageBox.Show("转换完毕");
}
// 提取视频帧
public void ExtractFrames(string videoFilePath, string outputFolderPath, int frameRate = 1)
{
var ffmpegPath = "ffmpeg.exe"; // 或者如果 FFmpeg 在你的环境变量路径中,直接使用 "ffmpeg"
var arguments = $"-i \"{videoFilePath}\" -q:v 1 -sws_flags lanczos \"{outputFolderPath}/frame_%04d.jpg\"";
using (var process = new Process())
{
process.StartInfo = new ProcessStartInfo
{
FileName = ffmpegPath,
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true // FFmpeg 喜欢通过 StdErr 输出日志
};
process.Start();
process.WaitForExit(); // 等待 FFmpeg 进程结束
}
}
// 处理图片
int videoMaskSize2 = 15;
public void ProcessImagesInFolder2(String folderPath, String process_path)
{
// 获取指定文件夹中所有的.jpg文件
string[] imageFiles = Directory.GetFiles(folderPath, "*.jpg");
// 循环处理所有的图片
foreach (string imageFile in imageFiles)
{
// 使用文件路径加载每个图片为Bitmap对象
using (Bitmap frame = new Bitmap(imageFile))
{
Bitmap bmp = new Bitmap(frame.Width, frame.Height);
Graphics graphics = Graphics.FromImage(bmp);
graphics.Clear(Color.Black);
int idxI = 0;
int idxJ = 0;
for (int m = 0; m < frame.Width; m += videoMaskSize2)
{
idxJ = 0;
int r = 0;
int g = 0;
int b = 0;
for (int n = 0; n < frame.Height; n += videoMaskSize2)
{
// 块内求和
for (int x = 0; x < videoMaskSize2; x++)
{
for (int y = 0; y < videoMaskSize2; y++)
{
Color c1;
if (m + x >= frame.Width || n + y >= frame.Height)
{
c1 = Color.FromArgb(0, 0, 0);
} else
{
c1 = frame.GetPixel(m + x, n + y);
}
r += c1.R;
g += c1.G;
b += c1.B;
}
}
// 求平均
r /= (videoMaskSize2 * videoMaskSize2);
g /= (videoMaskSize2 * videoMaskSize2);
b /= (videoMaskSize2 * videoMaskSize2);
// 画圈圈
graphics.FillEllipse(new SolidBrush(Color.FromArgb(r, g, b)),
idxI * videoMaskSize2, idxJ * videoMaskSize2, videoMaskSize2, videoMaskSize2);
idxJ++;
}
idxI++;
}
// 保存图片
string processedImagePath = Path.Combine(process_path, "processed_" + Path.GetFileName(imageFile));
bmp.Save(processedImagePath, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
}
private void btnCircle_Click(object sender, EventArgs e)
{
ProcessImagesInFolder2("frameMP4", "processedFrameMP4");
}
}
}
AVL writer类
c
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace TextDancing
{
public class AVIWriter
{
const string AVIFILE32 = "AVIFIL32";
private int _pfile = 0;
private IntPtr _ps = new IntPtr(0);
private IntPtr _psCompressed = new IntPtr(0);
private UInt32 _frameRate = 0;
private int _count = 0;
private UInt32 _width = 0;
private UInt32 _stride = 0;
private UInt32 _height = 0;
//avi标识
private UInt32 _fccType = 1935960438; // vids
private UInt32 _fccHandler = 808810089;// IV50
private Bitmap _bmp;
[DllImport(AVIFILE32)]
private static extern void AVIFileInit();
[DllImport(AVIFILE32)]
private static extern int AVIFileOpenW(ref int ptr_pfile, [MarshalAs(UnmanagedType.LPWStr)] string fileName, int flags, int dummy);
[DllImport(AVIFILE32)]
private static extern int AVIFileCreateStream(int ptr_pfile, out IntPtr ptr_ptr_avi, ref AVISTREAMINFOW ptr_streaminfo);
[DllImport(AVIFILE32)]
private static extern int AVIMakeCompressedStream(out IntPtr ppsCompressed, IntPtr aviStream, ref AVICOMPRESSOPTIONS ao, int dummy);
[DllImport(AVIFILE32)]
private static extern int AVIStreamSetFormat(IntPtr aviStream, Int32 lPos, ref BITMAPINFOHEADER lpFormat, Int32 cbFormat);
[DllImport(AVIFILE32)]
unsafe private static extern int AVISaveOptions(int hwnd, UInt32 flags, int nStreams, IntPtr* ptr_ptr_avi, AVICOMPRESSOPTIONS** ao);
[DllImport(AVIFILE32)]
private static extern int AVIStreamWrite(IntPtr aviStream, Int32 lStart, Int32 lSamples, IntPtr lpBuffer, Int32 cbBuffer, Int32 dwFlags, Int32 dummy1, Int32 dummy2);
[DllImport(AVIFILE32)]
private static extern int AVIStreamRelease(IntPtr aviStream);
[DllImport(AVIFILE32)]
private static extern int AVIFileRelease(int pfile);
[DllImport(AVIFILE32)]
private static extern void AVIFileExit();
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct AVISTREAMINFOW
{
public UInt32 fccType;
public UInt32 fccHandler;
public UInt32 dwFlags;
public UInt32 dwCaps;
public UInt16 wPriority;
public UInt16 wLanguage;
public UInt32 dwScale;
public UInt32 dwRate;
public UInt32 dwStart;
public UInt32 dwLength;
public UInt32 dwInitialFrames;
public UInt32 dwSuggestedBufferSize;
public UInt32 dwQuality;
public UInt32 dwSampleSize;
public UInt32 rect_left;
public UInt32 rect_top;
public UInt32 rect_right;
public UInt32 rect_bottom;
public UInt32 dwEditCount;
public UInt32 dwFormatChangeCount;
public UInt16 szName0;
public UInt16 szName1;
public UInt16 szName2;
public UInt16 szName3;
public UInt16 szName4;
public UInt16 szName5;
public UInt16 szName6;
public UInt16 szName7;
public UInt16 szName8;
public UInt16 szName9;
public UInt16 szName10;
public UInt16 szName11;
public UInt16 szName12;
public UInt16 szName13;
public UInt16 szName14;
public UInt16 szName15;
public UInt16 szName16;
public UInt16 szName17;
public UInt16 szName18;
public UInt16 szName19;
public UInt16 szName20;
public UInt16 szName21;
public UInt16 szName22;
public UInt16 szName23;
public UInt16 szName24;
public UInt16 szName25;
public UInt16 szName26;
public UInt16 szName27;
public UInt16 szName28;
public UInt16 szName29;
public UInt16 szName30;
public UInt16 szName31;
public UInt16 szName32;
public UInt16 szName33;
public UInt16 szName34;
public UInt16 szName35;
public UInt16 szName36;
public UInt16 szName37;
public UInt16 szName38;
public UInt16 szName39;
public UInt16 szName40;
public UInt16 szName41;
public UInt16 szName42;
public UInt16 szName43;
public UInt16 szName44;
public UInt16 szName45;
public UInt16 szName46;
public UInt16 szName47;
public UInt16 szName48;
public UInt16 szName49;
public UInt16 szName50;
public UInt16 szName51;
public UInt16 szName52;
public UInt16 szName53;
public UInt16 szName54;
public UInt16 szName55;
public UInt16 szName56;
public UInt16 szName57;
public UInt16 szName58;
public UInt16 szName59;
public UInt16 szName60;
public UInt16 szName61;
public UInt16 szName62;
public UInt16 szName63;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct AVICOMPRESSOPTIONS
{
public UInt32 fccType;
public UInt32 fccHandler;
public UInt32 dwKeyFrameEvery;
public UInt32 dwQuality;
public UInt32 dwBytesPerSecond;
public UInt32 dwFlags;
public IntPtr lpFormat;
public UInt32 cbFormat;
public IntPtr lpParms;
public UInt32 cbParms;
public UInt32 dwInterleaveEvery;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BITMAPINFOHEADER
{
public UInt32 biSize;
public Int32 biWidth;
public Int32 biHeight;
public Int16 biPlanes;
public Int16 biBitCount;
public UInt32 biCompression;
public UInt32 biSizeImage;
public Int32 biXPelsPerMeter;
public Int32 biYPelsPerMeter;
public UInt32 biClrUsed;
public UInt32 biClrImportant;
}
public class AviException : ApplicationException
{
public AviException(string s) : base(s) { }
public AviException(string s, Int32 hr)
: base(s)
{
if (hr == AVIERR_BADPARAM)
{
err_msg = "AVIERR_BADPARAM";
}
else
{
err_msg = "unknown";
}
}
public string ErrMsg()
{
return err_msg;
}
private const Int32 AVIERR_BADPARAM = -2147205018;
private string err_msg;
}
public Bitmap Create(string fileName, UInt32 frameRate, int width, int
height)
{
_frameRate = frameRate;
_width = (UInt32)width;
_height = (UInt32)height;
_bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
//锁定为24位位图
BitmapData bmpDat = _bmp.LockBits(new Rectangle(0, 0, width,
height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
_stride = (UInt32)bmpDat.Stride;
_bmp.UnlockBits(bmpDat);
AVIFileInit();
int hr = AVIFileOpenW(ref _pfile, fileName, 4097, 0);
if (hr != 0)
{
throw new AviException("Create错误!");
}
CreateStream();
SetOptions();
return _bmp;
}
public void AddFrame()
{
BitmapData bmpDat = _bmp.LockBits(
new Rectangle(0, 0, (int)_width, (int)_height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
int hr = AVIStreamWrite(_psCompressed, _count, 1,
bmpDat.Scan0,
(Int32)(_stride * _height),
0,
0,
0);
if (hr != 0)
{
throw new AviException("AVIStreamWrite");
}
_bmp.UnlockBits(bmpDat);
_count++;
}
public void LoadFrame(Bitmap nextframe)
{
_bmp = new Bitmap(nextframe);
}
public void Close()
{
AVIStreamRelease(_ps);
AVIStreamRelease(_psCompressed);
AVIFileRelease(_pfile);
AVIFileExit();
}
/// <summary>
/// 创建流文件
/// </summary>
private void CreateStream()
{
AVISTREAMINFOW strhdr = new AVISTREAMINFOW();
strhdr.fccType = _fccType;
strhdr.fccHandler = _fccHandler;
strhdr.dwFlags = 0;
strhdr.dwCaps = 0;
strhdr.wPriority = 0;
strhdr.wLanguage = 0;
strhdr.dwScale = 1;
strhdr.dwRate = _frameRate;
strhdr.dwStart = 0;
strhdr.dwLength = 0;
strhdr.dwInitialFrames = 0;
strhdr.dwSuggestedBufferSize = _height * _stride;
strhdr.dwQuality = 0xffffffff;
strhdr.dwSampleSize = 0;
strhdr.rect_top = 0;
strhdr.rect_left = 0;
strhdr.rect_bottom = _height;
strhdr.rect_right = _width;
strhdr.dwEditCount = 0;
strhdr.dwFormatChangeCount = 0;
strhdr.szName0 = 0;
strhdr.szName1 = 0;
int hr = AVIFileCreateStream(_pfile, out _ps, ref strhdr);
if (hr != 0)
{
throw new AviException("AVIFileCreateStream");
}
}
/// <summary>
/// 设置参数
/// </summary>
unsafe private void SetOptions()
{
AVICOMPRESSOPTIONS opts = new AVICOMPRESSOPTIONS();
opts.fccType = _fccType;
opts.fccHandler = 0;
opts.dwKeyFrameEvery = 0;
opts.dwQuality = 0;
opts.dwFlags = 0;
opts.dwBytesPerSecond = 0;
opts.lpFormat = new IntPtr(0);
opts.cbFormat = 0;
opts.lpParms = new IntPtr(0);
opts.cbParms = 0;
opts.dwInterleaveEvery = 0;
AVICOMPRESSOPTIONS* p = &opts;
AVICOMPRESSOPTIONS** pp = &p;
IntPtr x = _ps;
IntPtr* ptr_ps = &x;
AVISaveOptions(0, 0, 1, ptr_ps, pp);
int hr = AVIMakeCompressedStream(out _psCompressed, _ps, ref
opts, 0);
if (hr != 0)
{
throw new AviException("AVIMakeCompressedStream");
}
BITMAPINFOHEADER bi = new BITMAPINFOHEADER();
bi.biSize = 40;
bi.biWidth = (Int32)_width;
bi.biHeight = (Int32)_height;
bi.biPlanes = 1;
bi.biBitCount = 24;
bi.biCompression = 0;
bi.biSizeImage = _stride * _height;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
hr = AVIStreamSetFormat(_psCompressed, 0, ref bi, 40);
if (hr != 0)
{
throw new AviException("AVIStreamSetFormat", hr);
}
}
};
}
需要的工具
ffmpeg.exe