C#で作るガンマ補正で明るさを調整するコンソールアプリ

コンピュータ

画像ファイルをガンマ補正で明るさを調整するコマンドです。

ソースコード

ファイル名: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

コメント