Skip to content

Instantly share code, notes, and snippets.

@dolemite7
Last active June 1, 2021 15:57
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dolemite7/b50903c0002cc1c5c489364694c6c0cd to your computer and use it in GitHub Desktop.
Save dolemite7/b50903c0002cc1c5c489364694c6c0cd to your computer and use it in GitHub Desktop.
C# Video Thumbnail Sheet
/*
ThumbnailSheet uses MediaToolkit (https://github.com/AydinAdn/MediaToolkit) and Magick.NET (https://magick.codeplex.com/)
to create a sheet of timestamped thumbnails for a given video file.
Preview: http://imgur.com/a/iLGMs
Use Nuget to Install-Package MediaToolKit, and download a copy of Magick.NET, add it to your project's /bin folder and
create a reference to the .dll.
Usage example:
ThumbnailSheet oSheet = new ThumbnailSheet();
oSheet.CreateThumbnailSheet(@"d:\temp\imagemagicktemp\capture-burial-1.mp4");
*/
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Diagnostics;
using MediaToolkit;
using MediaToolkit.Model;
using MediaToolkit.Options;
using ImageMagick;
namespace Thumbnailer
{
public class ThumbnailSheet
{
internal static void Main(string[] args)
{
//MagickNET.Initialize(@"C:\MyProgram\MyImageMagickXmlFiles");
MagickNET.SetTempDirectory(@"d:\\temp\\imagemagicktemp\\");
}
public TimeSpan GetVideoLength(string inValue)
{
var inputFile = new MediaFile { Filename = @inValue };
using (var engine = new Engine())
{
engine.GetMetadata(inputFile);
}
return inputFile.Metadata.Duration;
}
public string CreateThumbnail(string inValue, double inSeconds)
{
var inputFile = new MediaFile { Filename = @inValue };
string outputFileName = inValue.Substring(0,inValue.LastIndexOf(".")) + ".jpg";
var outputFile = new MediaFile { Filename = @outputFileName };
using (var engine = new Engine())
{
engine.GetMetadata(inputFile);
var options = new ConversionOptions { Seek = TimeSpan.FromSeconds(inSeconds) };
if (!System.IO.File.Exists(outputFileName))
engine.GetThumbnail(inputFile, outputFile, options);
}
return outputFileName;
}
public void CreateThumbnails(string inFile, int inQty)
{
if (!System.IO.File.Exists(inFile))
{
Console.WriteLine("File not found.");
return;
}
double dSeconds = GetVideoLength(inFile).TotalSeconds;
double dSecondSplit = (dSeconds == 0 || inQty < 1) ? 0 : dSeconds / inQty;
int fileCount = 1;
string fileOut = "";
string fileNumber = "";
for (double x = 0; x < dSeconds; x += dSecondSplit)
{
fileOut = CreateThumbnail(inFile, x);
fileNumber = fileCount.ToString("000");
if (Math.Round(x) < Math.Round(dSeconds) && !System.IO.File.Exists(fileOut.Replace(".jpg", "-" + fileNumber + ".jpg")))
{
string str = TimeSpan.FromSeconds(x).ToString(@"hh\:mm\:ss");
StampImage(fileOut, str);
System.IO.File.Move(fileOut, fileOut.Replace(".jpg", "-" + fileNumber + ".jpg"));
}
fileCount++;
}
}
public void StampImage(string inFile, string inText)
{
string tempPath = inFile.Substring(0, inFile.LastIndexOf("\\") + 1);
string tempFilename = inFile.Substring(inFile.LastIndexOf("\\") + 1);
string tempFileprefix = tempFilename.Substring(0, tempFilename.LastIndexOf(".")) + "-";
var imageBackgroundColor = new MagickColor("Black");
var img = new MagickImage(inFile);
var imageFileName = tempPath + tempFileprefix + "-overlay.jpg";
using (MagickImage imgText = new MagickImage(inFile))
{
var drawable = new DrawableText(5, 5, inText);
var gravity = new DrawableGravity(Gravity.Southeast);
var font = new DrawableFont("Tahoma");
var antialias = new DrawableTextAntialias(true);
var size = new DrawableFontPointSize(48);
var color = new DrawableFillColor(Color.Black);
var strokecolor = new DrawableStrokeColor(Color.AliceBlue);
imgText.Draw(drawable, gravity, font, antialias, size, color, strokecolor);
imgText.Write(imageFileName);
}
System.IO.File.Delete(inFile);
System.IO.File.Move(imageFileName, inFile);
}
public void CreateThumbnailSheet(string inFilepath)
{
if (!System.IO.File.Exists(inFilepath))
{
Console.WriteLine("File not found.");
return;
}
Stopwatch sw = Stopwatch.StartNew();
CreateThumbnails(inFilepath, 16);
string tempPath = inFilepath.Substring(0, inFilepath.LastIndexOf("\\") + 1);
string tempFilename = inFilepath.Substring(inFilepath.LastIndexOf("\\") + 1);
string tempFileprefix = tempFilename.Substring(0, tempFilename.LastIndexOf(".")) + "-";
int tempWidth = 0;
int tempHeight = 0;
if (System.IO.File.Exists(tempPath + tempFileprefix + "sheet.png"))
System.IO.File.Delete(tempPath + tempFileprefix + "sheet.png");
List<string> lFiles = new List<string>();
foreach (string sFile in Directory.EnumerateFiles(tempPath, tempFileprefix + "*.jpg"))
lFiles.Add(sFile);
using (MagickImageCollection images = new MagickImageCollection())
{
foreach (string tempFile in lFiles)
{
MagickImage magickinput = new MagickImage(tempFile);
tempWidth = magickinput.Width;
tempHeight = magickinput.Height;
images.Add(magickinput);
}
tempWidth /= 2;
tempHeight /= 2;
MontageSettings ms = new MontageSettings();
//ms.Geometry = new MagickGeometry(string.Format("{0}x{1}", 200, 200));
ms.Geometry = new MagickGeometry(string.Format("{0}x{1}", tempWidth, tempHeight));
ms.TileGeometry = new MagickGeometry(string.Format("{0}x", 4));
ms.BackgroundColor = Color.Black;
ms.BorderColor = Color.DarkGray;
ms.BorderWidth = 2;
ms.Font = "Tahoma";
ms.FontPointsize = 20;
ms.FillColor = Color.LightGray;
ms.StrokeColor = Color.White;
ms.Title = tempFilename;
using (MagickImage montageResult = images.Montage(ms))
{
montageResult.Write(tempPath + tempFileprefix + "sheet.png");
}
}
foreach (string tempFile in lFiles)
{
if (System.IO.File.Exists(tempFile))
System.IO.File.Delete(tempFile);
}
sw.Stop();
Console.WriteLine("Montage complete: " + sw.Elapsed.TotalMilliseconds.ToString() + "ms.");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment