午後わてんのブログ

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

色相環画像作成、WriteableBitmapとImage.Clip

 
イメージ 1
一番右のが目的の色相環
いきなりImage4の画像を作成しようとしても難しくてできなかったので
Image2を作って、あとはImageクラスのClipプロパティにEllipseGeometryを指定して切り抜いて作成した
以前作ったRGBとHSVを相互変換するhsv.dllを参照に追加している
 
 
デザイン画面

f:id:gogowaten:20191213165122p:plain

StackPanelにImageを4つ並べただけ
 
 
MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using MyHSV;

namespace _20190325_色相環_WriteableBitmap
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            int width = 100;
            int height = 100;
            //赤Rect
            MyImage.Source = MakeRedRect(width, height);
            //色相環Rect
            MyImage2.Source = MakeHueRountRect(width, height);
            MyImage3.Source = MakeHueRountRect(width, height);
            MyImage4.Source = MakeHueRountRect(width, height);


            //切り抜いて円にする
            MyImage3.Clip = new EllipseGeometry(new Rect(0, 0, width, height));


            //切り抜いて円環にする、PathGeometry
            double ringWidth = 10;//リングの幅指定
            PathGeometry pg = new PathGeometry();
            //外側用Geometry作成追加
            pg.AddGeometry(new EllipseGeometry(new Rect(0, 0, width, height)));
            //内側用Geometry作成追加
            double xCenter = width / 2.0;//50
            double yCenter = height / 2.0;
            double xRadius = xCenter - ringWidth;//40
            double yRadius = yCenter - ringWidth;
            pg.AddGeometry(new EllipseGeometry(new Point(xCenter, yCenter), xRadius, yRadius));
            //切り抜き
            MyImage4.Clip = pg;

            //サイズ100x100、リング幅10で決め打ち
            //PathGeometry pg = new PathGeometry();
            //pg.AddGeometry(new EllipseGeometry(new Rect(0, 0, width, height)));
            //pg.AddGeometry(new EllipseGeometry(new Point(50, 50), 40, 40));//決め打ち
            //MyImage4.Clip = pg;


        }
        /// <summary>
        /// pixelsFormats.Rgb24の赤一色のBitmap作成
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        private BitmapSource MakeRedRect(int width, int height)
        {
            var wb = new WriteableBitmap(width, height, 96, 96, PixelFormats.Rgb24, null);
            int stride = wb.BackBufferStride;//横一列のバイト数、24bit = 8byteに横ピクセル数をかけた値
            byte[] pixels = new byte[height * stride * 8];//*8はbyteをbitにするから
            for (int i = 0; i < pixels.Length; i += 3)//
            {
                pixels[i] = 255;//  red
                pixels[i + 1] = 0;//green
                pixels[i + 2] = 0;//blue
            }
            wb.WritePixels(new Int32Rect(0, 0, width, height), pixels, stride, 0);
            return wb;
        }
        /// <summary>
        /// pixelsFormats.Rgb24の色相環作成用のBitmap作成
        /// 右が赤、時計回り
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        private BitmapSource MakeHueRountRect(int width, int height)
        {
            var wb = new WriteableBitmap(width, height, 96, 96, PixelFormats.Rgb24, null);
            //色情報用のバイト配列作成
            int stride = wb.BackBufferStride;//横一列のバイト数、24bit = 8byteに横ピクセル数をかけた値
            byte[] pixels = new byte[height * stride * 8];//*8はbyteをbitにするから

            //100x100のとき中心は50,50
            //ピクセル位置と画像の中心との差
            int xDiff = width / 2;
            int yDiff = height / 2;
            int p = 0;//今のピクセル位置の配列での位置
            for (int y = 0; y < height; y++)//y座標
            {
                for (int x = 0; x < width; x++)//x座標
                {
                    //今の位置の角度を取得、これが色相になる
                    double radian = Math.Atan2(y - yDiff, x - xDiff);
                    double kakudo = Degrees(radian);
                    //色相をColorに変換
                    Color c = HSV.HSV2Color(kakudo, 1.0, 1.0);
                    //バイト配列に色情報を書き込み
                    p = y * stride + x * 3;
                    pixels[p] = c.R;
                    pixels[p + 1] = c.G;
                    pixels[p + 2] = c.B;
                }
            }
            //バイト配列をBitmapに書き込み
            wb.WritePixels(new Int32Rect(0, 0, width, height), pixels, stride, 0);
            return wb;
        }


        //ラジアンを0~360の角度に変換
        public double Degrees(double radian)
        {
            double deg = radian / Math.PI * 180;
            if (deg < 0) deg += 360;
            return deg;
        }
    }
}
 
 
 
ピクセルの色の決定
画像の中心からのピクセル位置がわかれば三角関数Atan2ラジアンを得られる
ラジアンを角度に変換、角度はそのまま色相になるのであとは
色相をColor型に変換すればその位置のピクセルの色が決まる
 
画像サイズが100x100のとき中心位置は50,50
イメージ 3
決まり手:エクセル方眼紙+セル内改行
右下(100,100)の中心からの位置は(50, 50)、これのラジアン
Atan2(50,50) = 0.785398、角度に変換すると
Degrees(0.785398) = 45°、これが色相になる
彩度と明度は最大値にしたHSVColorに変換すると
HSV(45,100%,100%) = RGB(255,191,0)
 
イメージ 4
 
 
これで
イメージ 5
四角い色相環ができるので次は切り抜いて丸くする
 
イメージ 6
Sourceに色相環の画像を指定したMyImage3、24行目
ImageクラスのClipプロパティにEllipseGeometryで円に切り抜き、29行目
たったこれだけで
 
イメージ 7
自動でアンチエイリアスもかかるから切り抜きの境界もなめらか
 
ここから内側も円形に取り除けば円環にできる
元の四角形からは合計2つのEllipseGeometryを使うので
PathGeometryを作って、そこにAddGeometryを使って
2つのEllipseGeometryを追加したものを
Image.Clipにしていする
 
イメージ 8
これで
 
イメージ 9
目的の色相環になった
 
 
イメージ 10
2つ目のEllipseGeometryの大きさを40から30に小さくすると
 
イメージ 11
取り除かれる円が小さくなって、そのぶん太くなる
意外に手軽にできるねえ
 
イメージ 12
200x200サイズ
 
 
彩度の変化をつけてみた
イメージ 14
イメージ 13
中心彩度0、外側彩度100になっているはず
 
 
今回もできてみればラクなんだけど
最初はなんか楽な方法はないかなあってググったけど難しいのしか見つからなくて
思いついたのがグラデーションのPenやBrushを使って作ろうって

f:id:gogowaten:20191213165221p:plain

6つのPathを組み合わせて円環になるように配置して
それぞれにグラデーションのBrushを作成して塗ってみたけど
うまくできなかった
イメージ 15
それっぽく見えるけど違う
Brushは直線のグラデーションしかできないので
円に沿ったグラデーションには向かない
他に方法が思いつかなかったので、めんどくさそうな方法だけど、1ピクセルごとの位置から計算して塗っていくわって試してみたら意外にあっさりできた
画像の切り抜きも意外に簡単だった
 
 
DLLの参照とギットハブ
HSVからRGBへの変換は、以前作ったhsv.dllを参照に追加して利用したのが意外にラクにできた原因なんだけど
この参照に追加したDLLがギットハブではどういう扱いになるのかよくわからん
見た感じだとどこにもDLLのファイルが見つからない
プロジェクトに「既存の項目を追加」からDLLを追加して、それを参照に追加とかすればいいんかなあ
 
 

参照したところ
 
C#で画像を描いてみた(WPFでWritableBitmap編)
https://water2litter.net/gin/?p=984
 
 
 
 

ギットハブ
HSV.dll
HSV.zip(ヤフーボックス)


 

関連記事
2019/04/02は1週間後
切り抜きClip、GeometryGroupとCombinedGeometry ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15921152.html
 
2019/04/01は1週間後
画像の色相の状態をレーダーチャートふうに表示してみた ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15920156.html
2019/04/04は10日後
画像の色相を円形ヒストグラム、扇形(パイ型)グラフで表示するアプリできた ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15923169.html
イメージ 17
 
 
2018/2/18は1ヶ月前
dllファイル(クラスライブラリ.NET framework)の作り方と使うまでの手順メモ ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15377219.html
2018/2/20は1ヶ月前
WPF、Color(RGB)とHSVを相互変換するdll作ってみた、オブジェクトブラウザ使ってみた ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15380324.html
2019/3/1は3週間前
画像で使われている色を3D散布図で表示してみた、Pythonとmatplotlibすごい ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15887088.html