午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

画像の色変更機能を追加した、OpaOpaOpasity

OpaOpaOpasity 2.1.0

色変換できるようにした

色変換



成果物

2023/07/27追記

グランツーリスモ4 ミッションレース12 4'02.874 ガルフカラー仕様(PCSX2のテクスチャ置換使用)のフォードGT LM レースカー Spec ⅡでMission 12 - YouTube

www.youtube.com 無彩色の変換にも対応したので、黒のラインを橙に変換できた

2023/07/27追記ここまで

ダウンロード先

Releases · gogowaten/OpaOpaOpasity
ここのOpaOpaOpasity2.1.0.0.7z
github.com



動作、作成環境



変更点

png以外の画像も変換できるようにした

png形式以外にも対応
jpg,bmp,gif,tiff,wdpに対応

仕様

  • 拡張子の文字列で判定している
  • 保存形式はpngのみ

色変換機能追加

色変換

タブで機能切り替え

不透明度変更タブ

変換ボタン

色変更設定

使う色空間はHSL
色相、Hue、0から360度で設定
彩度、Saturation、0から1.0で設定、色の鮮やかさ、0で無彩色
輝度(明度)、Lightness、0から1.0で設定、色の明るさ、0でまっくろ、1.0で真っ白、0.5で…原色?あと、アプリでは明度って書いたけど、輝度が正しいみたい?

範囲設定

変換したい色の範囲を指定する
下限と上限の間の色が変換対象になる

0(360)をまたぐ色相の範囲指定

色相は円環なので赤(色相0)を中心に前後30度を指定する場合は
まず、0の後ろ30は0+30=30でいいとして、0のまえは0-30=-30だけど、0は360でもあるので360-30=330なので
下限を330、上限を30、これでいい

変換設定

どんな色にするのかと、計算方法の指定
指定にした場合は、元の色に関係なく指定した値にする。
加算にした場合は、-1.0から+1.0を指定できる。これを元の値に加算する。また、加算した結果が0.0未満や1.0より大きい場合は、それぞれ0.0と1.0に丸める


変換例

全体を青にする
範囲設定
すべてを対象にしたいので
色相:0から360
彩度:0から1
明度:0から1

変換設定
青の色相は240度、鮮やかさや明るさは元の色を引き継ぎたいので、0を加算
色相:240を指定
彩度:0を加算
明度:0を加算

全体を青にする


いちごの赤だけ青にする
変換範囲
赤の色相は0、いちごの赤は0ぴったりじゃなくて、0付近なので0から40(橙)にして、彩度と明度はすべてにしてみる
色相:0から40
彩度:0から1
明度:0から1

変換設定、青の色相は240を指定、彩度と明度も元の色を引き継ぎたいので、これもさっきと同じ
これで変換してみると

赤を青に変換
プランターまで青くなっているけど、これはこれでいいかも
プランターの色は赤と言っても白っぽい、これは明度が高いってことなので、明度の範囲の上限を下げればいい
明度:0から0.5
これで変換
明度範囲0から0.5
いちごの一部まで除外されてしまったにも関わらず、プランターの大部分は範囲内、ってことは彩度の範囲!
プランターは鮮やかではないので、彩度の下限を上げて0.6、ついでに明度も少し戻して0.8
彩度:0.6から1
明度:0から0.8
この変換でちょうどよくなった
これがホントのブルーベリー

もうひと手間、いちごのつぶつぶが見えるように変換設定の明度を0.2加算

より自然?なブルーベリー完成


無彩色の色変換

無彩色を赤に変換
Pixtack紫陽花の色変換ではこれができなかった

その他変更点

エラー減ったはず
並列処理を単純化した

参照したところ

色の変換(カラーコード・RGB・HSV・HSL) / ツール - Hirota Yano

yanohirota.com RGBとHSLの変換式は、ここの説明がわかりやすかった

RGBとHSLの相互変換コード

MathHSLクラス

using System;

namespace OpaOpaOpasity
{

    public class MathHSL
    {
        /// <summary>
        /// RGBをHSLに変換
        /// h = 0 to 360.0, s,l = 0 to 1.0
        /// 無彩色時のhは360で返す
        /// </summary>
        /// <param name="r"></param>
        /// <param name="g"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static (double h, double s, double l) Rgb2Hsl(byte r, byte g, byte b)
        {
            byte min = Math.Min(b, Math.Min(r, g));
            byte max = Math.Max(b, Math.Max(r, g));

            double total = max + min;
            //輝度L
            double l = total / 2.0 / 255;

            double h;
            double s;
            //無彩色
            if (max == min)
            {
                //未定義
                h = 0.0; s = 0.0;
                //
                //h = 360.0;s = 0.0;
            }
            //有彩色
            else
            {
                double diff = max - min;
                //色相H
                if (max == r)
                {
                    h = (g - b) / diff * 60;
                    if (h < 0) { h += 360.0; }
                }
                else if (max == g)
                {
                    h = (b - r) / diff * 60 + 120;
                }
                else
                {
                    h = (r - g) / diff * 60 + 240;
                }

                //彩度S
                if (0 <= l && l <= 0.5)
                {
                    s = diff / total;
                }
                else
                {
                    s = diff / (510 - total);
                }
            }
            return (h, s, l);
        }



        //色の変換(カラーコード・RGB・HSV・HSL) / ツール - Hirota Yano
        //        https://yanohirota.com/color-converter/

        public static (byte r, byte g, byte b) Hsl2Rgb(double h, double s, double l)
        {
            double hue = h;
            if (hue == 360) { hue = 0; }

            double lum = l;
            if (0.5 <= l)
            {
                lum = 1.0 - l;
            }

            double max = 255 * (l + lum * s);
            double min = 255 * (l - lum * s);
            double diff = max - min;
            double rr, gg, bb;
            if (0 <= hue && hue < 60)
            {
                rr = max;
                gg = FFF(hue);
                bb = min;
            }
            else if (60 <= hue && hue < 120)
            {
                rr = FFF(120 - hue);
                gg = max;
                bb = min;
            }
            else if (120 <= hue && hue < 180)
            {
                rr = min;
                gg = max;
                bb = FFF(hue - 120);
            }
            else if (180 <= hue && hue < 240)
            {
                rr = min;
                gg = FFF(240 - hue);
                bb = max;
            }
            else if (240 <= hue && hue < 300)
            {
                rr = FFF(hue - 240);
                gg = min;
                bb = max;
            }
            else if (300 <= hue && hue < 360)
            {
                rr = max;
                gg = min;
                bb = FFF(360 - hue);
            }
            else
            {
                rr = 0;
                gg = 0;
                bb = 0;
            }

            return (
                (byte)Math.Round(rr, MidpointRounding.AwayFromZero),
                (byte)Math.Round(gg, MidpointRounding.AwayFromZero),
                (byte)Math.Round(bb, MidpointRounding.AwayFromZero));

            double FFF(double x)
            {
                return (x / 60 * diff) + min;
            }


        }
    }
}

HSLを扱うアプリでは、これをコピペで使っていこうと思う


感想

Pixtack紫陽花ではできなかった無彩色の色変換、これが目的だった!
あとは彩度と明度の加算もなかった(維持と指定のみの)はずなので、これで変換に柔軟性が出せた
見た目や操作性がいまいちだけど、機能は追加できたからヨシ!


関連記事

前回のWPF記事は一週間前
gogowaten.hatenablog.com

最初のOpaOpaOpasityは一ヶ月前
gogowaten.hatenablog.com