画像ファイルをガンマ補正で明るさを調整するコマンドです。
ソースコード
ファイル名:gamma.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenCvSharp4" Version="4.11.0.20250507" />
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.11.0.20250507" />
</ItemGroup>
</Project>
ファイル名:Opt.cs
using System.ComponentModel;
using System.Globalization;
public class Opt
{
public string Input { get; set; } = "";
public string? Output { get; set; } = null;
public double Gamma { get; set; } = 0.75;
public static Opt ParseArgs(string[] a)
{
var o = new Opt();
string? key = null;
double gamma = 0.0;
foreach (var token in a)
{
if (token.StartsWith("-"))
{
key = token;
}
else
{
switch (key)
{
case "-i": case "--in": o.Input = token; break;
case "-o": case "--out": o.Output = token; break;
case "-g": case "--gamma": double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out gamma); o.Gamma = gamma; break;
default:
// 位置引数互換
if (string.IsNullOrEmpty(o.Input)) o.Input = token;
else if (o.Output is null) o.Output = token;
break;
}
key = null;
}
}
return o;
}
public static void PrintHelp()
{
Console.Error.WriteLine(
@"Usage:
gamma -i <input> [options]
gamma <input> [output.png]
Options:
-i, --in <path> 入力ファイル
-o, --out <path> 出力ファイル(省略時: <input>_gmma.png)
-g, --gamma <double> ガンマ値(>1.0 暗く <1.0明るく)
");
}
}
ファイル名:Program.cs
// C#で作るガンマ補正で明るさを調整するコンソールアプリ
// ビルドコマンド
// dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true /p:IncludeAllContentForSelfExtract=true /p:SelfContained=true --output "exeの出力先のディレクトリ"
using OpenCvSharp;
class Program
{
static void Main(string[] args)
{
if (args.Length == 0 || args.Contains("-h") || args.Contains("--help"))
{
Opt.PrintHelp();
Environment.Exit(1);
}
var opt = Opt.ParseArgs(args);
if (string.IsNullOrWhiteSpace(opt.Input))
{
Opt.PrintHelp();
Environment.Exit(1);
}
string output = opt.Output ?? Path.Combine(
Path.GetDirectoryName(opt.Input) ?? "",
Path.GetFileNameWithoutExtension(opt.Input) + "_gamma.png");
imageFilter(opt.Input, output, opt.Gamma);
Console.WriteLine(output);
}
static void imageFilter(string inputPath, string outputPath, double gamma)
{
using var src = Cv2.ImRead(inputPath);
using var dst = ApplyGamma(src, gamma);
Cv2.ImWrite(outputPath, dst);
}
static Mat Ensure8U(Mat src)
{
if (src.Depth() == MatType.CV_8U) return src;
var tmp = new Mat();
// 0..1系は255倍、0..255系はそのままなど元データのレンジに応じて調整
src.ConvertTo(tmp, MatType.CV_8U, 255.0);
return tmp;
}
// 一般的な定義: γ<1で明るく
static byte[] BuildGammaLut(double gamma)
{
const int N = 256;
var lut = new byte[N];
if (gamma <= 0) { // 0や負は無効 -> 恒等
for (int i = 0; i < N; i++) lut[i] = (byte)i;
return lut;
}
for (int i = 0; i < N; i++) {
double x = i / 255.0;
double y = Math.Pow(x, gamma);
int v = (int)Math.Round(y * 255.0);
lut[i] = (byte)(v < 0 ? 0 : (v > 255 ? 255 : v));
}
return lut;
}
static Mat ApplyGamma(Mat src, double gamma)
{
using var src8 = Ensure8U(src);
var lut = BuildGammaLut(gamma);
// BGRAのAは保持
if (src8.Channels() == 4)
{
Cv2.Split(src8, out Mat[] ch);
Cv2.LUT(ch[0], lut, ch[0]);
Cv2.LUT(ch[1], lut, ch[1]);
Cv2.LUT(ch[2], lut, ch[2]);
using var dst = new Mat();
Cv2.Merge(ch, dst);
foreach (var c in ch) c.Dispose();
return dst;
}
// 1ch, 3chはそのままLUT適用(3chは各チャネルに同一LUT)
var dstMat = new Mat();
Cv2.LUT(src8, lut, dstMat);
return dstMat;
}
}
ビルド
dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true /p:IncludeAllContentForSelfExtract=true /p:SelfContained=true --output "exeの出力先のディレクトリ"
実行コマンド
gamma -i 画像ファイルのパス -g ガンマ値
実行イメージ
・加工前画像
・ガンマ値0.2
・ガンマ値2.0
コメント