Files
flowframes/Code/Utilities/ColorDataUtils.cs
2022-10-14 09:00:47 +02:00

426 lines
19 KiB
C#

using Flowframes.Data;
using Flowframes.IO;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.Utilities
{
class ColorDataUtils
{
// public static async Task<VideoColorData> GetColorData(string path)
// {
// VideoColorData data = new VideoColorData();
//
// AvProcess.FfprobeSettings settings = new AvProcess.FfprobeSettings() { Args = $"-show_frames -select_streams v:0 -read_intervals \"%+#1\" {path.Wrap()}", LogLevel = "quiet" };
// string infoFfprobe = await AvProcess.RunFfprobe(settings);
//
// string[] linesFfprobe = infoFfprobe.SplitIntoLines();
//
// foreach (string line in linesFfprobe)
// {
// if (line.Contains("color_transfer="))
// data.ColorTransfer = GetColorTransfer(line.Split('=').Last());
//
// else if (line.Contains("color_space="))
// data.ColorMatrixCoeffs = GetMatrixCoeffs(line.Split('=').Last());
//
// else if (line.Contains("color_primaries="))
// data.ColorPrimaries = GetColorPrimaries(line.Split('=').Last());
//
// else if (line.Contains("color_range="))
// data.ColorRange = GetColorRange(line.Split('=').Last());
//
// else if (line.Contains("red_x="))
// data.RedX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("red_y="))
// data.RedY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("green_x="))
// data.GreenX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("green_y="))
// data.GreenY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("blue_x="))
// data.BlueY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("blue_y="))
// data.BlueX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("white_point_x="))
// data.WhiteY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("white_point_y="))
// data.WhiteX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("max_luminance="))
// data.LumaMax = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("min_luminance="))
// data.LumaMin = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("max_content="))
// data.MaxCll = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("max_average="))
// data.MaxFall = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
// }
//
// string infoMkvinfo = await AvProcess.RunMkvInfo($"{path.Wrap()}", OS.NmkoderProcess.ProcessType.Secondary);
//
// if (infoMkvinfo.Contains("+ Video track"))
// {
// string[] lines = infoMkvinfo.Split("+ Video track")[1].Split("+ Track")[0].Split("+ Tags")[0].SplitIntoLines();
//
// foreach (string line in lines)
// {
// if (line.Contains("+ Colour transfer:"))
// data.ColorTransfer = ValidateNumber(line.Split(':')[1]).GetInt();
//
// else if (line.Contains("+ Colour matrix coefficients:"))
// data.ColorMatrixCoeffs = ValidateNumber(line.Split(':')[1]).GetInt();
//
// else if (line.Contains("+ Colour primaries:"))
// data.ColorPrimaries = ValidateNumber(line.Split(':')[1]).GetInt();
//
// else if (line.Contains("+ Colour range:"))
// data.ColorRange = ValidateNumber(line.Split(':')[1]).GetInt();
//
// else if (line.Contains("+ Red colour coordinate x:"))
// data.RedX = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Red colour coordinate y:"))
// data.RedY = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Green colour coordinate x:"))
// data.GreenX = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Green colour coordinate y:"))
// data.GreenY = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Blue colour coordinate y:"))
// data.BlueY = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Blue colour coordinate x:"))
// data.BlueX = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ White colour coordinate y:"))
// data.WhiteY = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ White colour coordinate x:"))
// data.WhiteX = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Maximum luminance:"))
// data.LumaMax = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Minimum luminance:"))
// data.LumaMin = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Maximum content light:"))
// data.MaxCll = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Maximum frame light:"))
// data.MaxFall = ValidateNumber(line.Split(':')[1]);
// }
// }
//
// return data;
// }
private static string FractionToFloat(string fracString)
{
string[] fracNums = fracString.Split('/');
return ((float)fracNums[0].GetInt() / (float)fracNums[1].GetInt()).ToString("0.#######", new CultureInfo("en-US"));
}
private static string ValidateNumber(string numStr)
{
return Double.Parse(numStr, NumberStyles.Float, CultureInfo.InvariantCulture).ToString("0.#######", new CultureInfo("en-US"));
}
// public static async Task SetColorData(string path, VideoColorData d)
// {
// try
// {
// string tmpPath = IoUtils.FilenameSuffix(path, ".tmp");
//
// List<string> args = new List<string>();
//
// args.Add($"-o {tmpPath.Wrap()}");
// args.Add($"--colour-matrix 0:{d.ColorMatrixCoeffs}");
// args.Add($"--colour-transfer-characteristics 0:{d.ColorTransfer}");
// args.Add($"--colour-primaries 0:{d.ColorPrimaries}");
// args.Add($"--colour-range 0:{d.ColorRange}");
// if (!string.IsNullOrWhiteSpace(d.LumaMax)) args.Add($"--max-luminance 0:{d.LumaMax}");
// if (!string.IsNullOrWhiteSpace(d.LumaMin)) args.Add($"--min-luminance 0:{d.LumaMin}");
// if (!string.IsNullOrWhiteSpace(d.RedX)) args.Add($"--chromaticity-coordinates 0:{d.RedX},{d.RedY},{d.GreenX},{d.GreenY},{d.BlueX},{d.BlueY}");
// if (!string.IsNullOrWhiteSpace(d.RedX)) args.Add($"--white-colour-coordinates 0:{d.WhiteX},{d.WhiteY}");
// if (!string.IsNullOrWhiteSpace(d.MaxCll)) args.Add($"--max-content-light 0:{d.MaxCll}");
// if (!string.IsNullOrWhiteSpace(d.MaxFall)) args.Add($"--max-frame-light 0:{d.MaxFall}");
// args.Add($"{path.Wrap()}");
//
// await AvProcess.RunMkvMerge(string.Join(" ", args), OS.NmkoderProcess.ProcessType.Primary, true);
//
// if (!File.Exists(tmpPath))
// {
// Logger.Log($"Error: Muxing failed.");
// return;
// }
//
// int filesizeDiffKb = (int)((Math.Abs(new FileInfo(path).Length - new FileInfo(tmpPath).Length)) / 1024);
// double filesizeFactor = (double)(new FileInfo(tmpPath).Length) / (double)(new FileInfo(path).Length);
// Logger.Log($"{MethodBase.GetCurrentMethod().DeclaringType}: Filesize ratio of remuxed file against original: {filesizeFactor}", true);
//
// if (filesizeDiffKb > 1024 && (filesizeFactor < 0.95d || filesizeFactor > 1.05d))
// {
// Logger.Log($"Warning: Output file size differs by >1MB is not within 5% of the original file's size! Won't delete original to be sure.");
// }
// else
// {
// File.Delete(path);
// File.Move(tmpPath, path);
// }
// }
// catch (Exception e)
// {
// Logger.Log($"SetColorData Error: {e.Message}\n{e.StackTrace}");
// }
// }
public static int GetColorPrimaries(string s) // Defined by the "Color primaries" section of ISO/IEC 23091-4/ITU-T H.273
{
s = s.Trim().ToLowerInvariant();
if (s == "bt709") return 1;
if (s == "bt470m") return 4;
if (s == "bt470bg") return 5;
if (s == "bt601") return 6;
if (s == "smpte240m") return 7;
if (s == "film") return 8;
if (s == "bt2020") return 9;
if (s == "smpte428") return 10;
if (s == "smpte431") return 11;
if (s == "smpte432") return 12;
return 2; // Fallback: 2 = Unspecified
}
public static int GetColorTransfer(string s) // Defined by the "Transfer characteristics" section of ISO/IEC 23091-4/ITU-T H.273
{
s = s.Trim().ToLowerInvariant();
if (s == "bt709") return 1;
if (s == "gamma22" || s == "bt470m") return 4;
if (s == "gamma28" || s == "bt470bg") return 5; // BT.470 System B, G (historical)
if (s == "bt601" || s == "smpte170m") return 6; // BT.601
if (s == "smpte240m") return 7; // SMPTE 240 M
if (s == "linear") return 8; // Linear
//if (s == "?") return 9; // Logarithmic(100 : 1 range)
//if (s == "?") return 10; // Logarithmic (100 * Sqrt(10) : 1 range)
if (s == "iec61966-2-4") return 11; // IEC 61966-2-4
if (s == "bt1361" || s == "bt1361e") return 12; // BT.1361
if (s == "srgb") return 13; // SRGB
if (s == "bt2020-10") return 14; // BT.2020 10-bit systems
if (s == "bt2020-12") return 15; // BT.2020 12-bit systems
if (s == "smpte2084") return 16; // SMPTE ST 2084, ITU BT.2100 PQ
if (s == "smpte428") return 17; // SMPTE ST 428
if (s == "bt2100") return 18; // BT.2100 HLG, ARIB STD-B67
return 2; // Fallback: 2 = Unspecified
}
public static int GetMatrixCoeffs(string s) // Defined by the "Matrix coefficients" section of ISO/IEC 23091-4/ITU-T H.27
{
s = s.Trim().ToLowerInvariant();
if (s == "bt709") return 1;
if (s == "fcc") return 4; // US FCC 73.628
if (s == "bt470bg") return 5; // BT.470 System B, G (historical)
if (s == "bt601" || s == "smpte170m") return 6; // BT.601
if (s == "smpte240m") return 7; // SMPTE 240 M
if (s == "ycgco") return 8; // YCgCo
if (s == "bt2020ncl" || s == "bt2020nc") return 9; // BT.2020 non-constant luminance, BT.2100 YCbCr
if (s == "bt2020") return 10; // BT.2020 constant luminance
if (s == "smpte2085") return 11; // SMPTE ST 2085 YDzDx
// 12: MC_CHROMAT_NCL - Chromaticity-derived non-constant luminance
// 13: MC_CHROMAT_CL - Chromaticity-derived constant luminance
// 14: MC_ICTCP BT.2100 - ICtCp
return 2; // Fallback: 2 = Unspecified
}
public static int GetColorRange(string s) // Defined by the "Matrix coefficients" section of ISO/IEC 23091-4/ITU-T H.27
{
s = s.Trim().ToLowerInvariant();
if (s == "tv") return 1; // TV
if (s == "pc") return 2; // PC/Full
return 0; // Fallback: Unspecified
}
public static string FormatForAom(string colorspace)
{
return colorspace.Replace("bt2020-10", "bt2020-10bit").Replace("bt2020-12", "bt2020-12bit");
}
#region Get string from int
public static string GetColorPrimariesString(int n)
{
switch (n)
{
case 1: return "bt709";
case 4: return "bt470m";
case 5: return "bt470bg";
case 6: return "bt601";
case 7: return "smpte240m";
case 8: return "film";
case 9: return "bt2020";
case 10: return "smpte428";
case 11: return "smpte431";
case 12: return "smpte432";
}
return "";
}
public static string GetColorTransferString(int n)
{
switch (n)
{
case 1: return "bt709";
case 4: return "gamma22"; // "bt470m"
case 5: return "gamma28"; // "bt470bg"
case 6: return "bt601"; // "smpte170m"
case 7: return "smpte240m";
case 8: return "linear";
case 11: return "iec61966-2-4";
case 12: return "bt1361";
case 13: return "srgb";
case 14: return "bt2020-10";
case 15: return "bt2020-12";
case 16: return "smpte2084";
case 17: return "smpte428";
case 18: return "bt2100";
}
return "";
}
public static string GetColorMatrixCoeffsString(int n)
{
switch (n)
{
case 1: return "bt709";
case 4: return "fcc";
case 5: return "bt470bg";
case 6: return "bt601";
case 7: return "smpte240m";
case 8: return "ycgco";
case 9: return "bt2020ncl";
case 10: return "bt2020";
}
return "";
}
public static string GetColorRangeString(int n)
{
switch (n)
{
case 1: return "tv";
case 2: return "pc";
}
return "";
}
#endregion
#region Get friendly name from int
public static string GetColorPrimariesName(int n)
{
switch (n)
{
case 1: return "BT.709";
case 2: return "Unspecified";
case 4: return "BT.470 System B, G (historical)";
case 5: return "BT.470 System M (historical)";
case 6: return "BT.601";
case 7: return "SMPTE 240";
case 8: return "Generic film (color filters using illuminant C)";
case 9: return "BT.2020, BT.2100";
case 10: return "SMPTE 428 (CIE 1921 XYZ)";
case 11: return "SMPTE RP 431-2";
case 12: return "SMPTE EG 432-1";
case 22: return "EBU Tech. 3213-E";
}
return "Unknown";
}
public static string GetColorTransferName(int n)
{
switch (n)
{
case 1: return "BT.709";
case 2: return "Unspecified";
case 4: return "BT.470 System B, G (historical)";
case 5: return "BT.470 System M (historical)";
case 6: return "BT.601";
case 7: return "SMPTE 240 M";
case 8: return "Linear";
case 9: return "Logarithmic (100 : 1 range)";
case 10: return "Logarithmic (100 * Sqrt(10) : 1 range)";
case 11: return "IEC 61966-2-4";
case 12: return "BT.1361";
case 13: return "sRGB or sYCC";
case 14: return "BT.2020 10-bit systems";
case 15: return "BT.2020 12-bit systems";
case 16: return "SMPTE ST 2084, ITU BT.2100 PQ";
case 17: return "SMPTE ST 428";
case 18: return "BT.2100 HLG, ARIB STD-B67";
}
return "Unknown";
}
public static string GetColorMatrixCoeffsName(int n)
{
switch (n)
{
case 1: return "BT.709";
case 2: return "Unspecified";
case 4: return "US FCC 73.628";
case 5: return "BT.470 System B, G (historical)";
case 6: return "BT.601";
case 7: return "SMPTE 240 M";
case 8: return "YCgCo";
case 9: return "BT.2020 non-constant luminance, BT.2100 YCbCr";
case 10: return "BT.2020 constant luminance";
case 11: return "SMPTE ST 2085 YDzDx";
case 12: return "Chromaticity-derived non-constant luminance";
case 13: return "Chromaticity-derived constant luminance";
case 14: return "BT.2100 ICtCp";
}
return "Unknown";
}
public static string GetColorRangeName(int n)
{
switch (n)
{
case 0: return "Unspecified";
case 1: return "TV (Limited)";
case 2: return "PC (Full)";
}
return "Unknown";
}
#endregion
}
}