明るさを補正するツールを作りました。
同じような考え方で、ガンマ補正を行って見ました。
GammaMatch.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- 追加 ここから -->
<PackAsTool>true</PackAsTool>
<ToolCommandName>GammaMatch</ToolCommandName>
<IsPackable>true</IsPackable>
<PackageId>GammaMatch</PackageId>
<Version>1.0.0</Version>
<!-- 追加 ここまで -->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenCvSharp4" Version="4.13.0.20260308" />
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.13.0.20260302" />
</ItemGroup>
</Project>
Program.cs
// 画像の明るさをガンマ補正するCLIツール
using OpenCvSharp;
if (args.Length < 3)
{
Console.WriteLine("GammaMatch reference input output");
return;
}
string refPath = args[0];
string srcPath = args[1];
string outPath = args[2];
var refImg = Cv2.ImRead(refPath, ImreadModes.Unchanged);
var srcImg = Cv2.ImRead(srcPath, ImreadModes.Unchanged);
if (refImg.Empty() || srcImg.Empty())
{
Console.WriteLine("image load error");
return;
}
// グレースケール化(平均輝度比較用)
var refGray = ToGray(refImg);
var srcGray = ToGray(srcImg);
// 平均輝度
double refAvg = Cv2.Mean(refGray).Val0;
double srcAvg = Cv2.Mean(srcGray).Val0;
// 0~1へ正規化
double refNorm = Clamp01(refAvg / 255.0);
double srcNorm = Clamp01(srcAvg / 255.0);
// 近似的にガンマ値を求める
double gamma = Math.Log(srcNorm) / Math.Log(refNorm);
// 異常値対策
if (double.IsNaN(gamma) || double.IsInfinity(gamma) || gamma <= 0)
{
gamma = 1.0;
}
Console.WriteLine($"ref avg = {refAvg:F2}");
Console.WriteLine($"src avg = {srcAvg:F2}");
Console.WriteLine($"gamma = {gamma:F4}");
// LUT作成
var lut = CreateGammaLut(gamma);
// 補正
var dst = new Mat();
Cv2.LUT(srcImg, lut, dst);
// 保存
Cv2.ImWrite(outPath, dst);
Console.WriteLine("done");
static Mat ToGray(Mat src)
{
if (src.Channels() == 1)
return src.Clone();
if (src.Channels() == 3)
return src.CvtColor(ColorConversionCodes.BGR2GRAY);
if (src.Channels() == 4)
return src.CvtColor(ColorConversionCodes.BGRA2GRAY);
throw new NotSupportedException($"unsupported channels: {src.Channels()}");
}
static double Clamp01(double value)
{
const double eps = 1e-6;
if (value < eps) return eps;
if (value > 1.0 - eps) return 1.0 - eps;
return value;
}
static Mat CreateGammaLut(double gamma)
{
double invGamma = 1.0 / gamma;
double lift = 0.06;
var table = new byte[256];
for (int i = 0; i < 256; i++)
{
double x = i / 255.0;
// 黒持ち上げ
x = (x + lift) / (1.0 + lift);
// ガンマ補正
double y = Math.Pow(x, invGamma);
int v = (int)Math.Round(y * 255.0);
v = Math.Clamp(v, 0, 255);
table[i] = (byte)v;
}
return Mat.FromArray(table);
}
/*
プロジェクトの作成
dotnet new console
dotnet add package OpenCvSharp4
dotnet add package OpenCvSharp4.runtime.win
ビルド&インストール
dotnet pack -c Release
dotnet tool install --global GammaMatch --add-source .\bin\Release
使い方
GammaMatch.exe reference.png input.png output.png
*/
試したところ、Real-ESRGANの超解像処理で暗くなった画像は、
こちらのガンマ補正を使ったほうが良好な結果が得られました。


コメント