色相範囲分割数
360分割、こっちのほうがより正確なはず、だけど見た目的には72くらいが面白いかな
色相0(赤)の単色画像の
12分割表示
色相0の範囲は、0を中心にプラスマイナス15度なので
0-15=-15=-15+360=345
0+15=15
345度から時計回りに15度までの範囲で表示している
360分割表示だと
色相90のSVグラデーション
中心付近が太くなっているから色相90から少し外れた色があるみたい
12分割
でも
72分割
360分割で詳しく
720分割
前回はレーダーチャートふうに表示してイマイチだった
表示方法は前回のレーダーチャートとほとんど同じ、違うのは切り抜き方を扇形(パイ型)にしただけなんだよねえ
青系は全体の10~20%って言われるとあっているけど、少なく見えるのは中心からの距離が遠いほどグラフの面積が大きくなるからだねえ
もしかして扇形(パイ型)じゃなくて円や四角形のほうがいいのかも?
今回に至るまで
2019/4/1は3日前の
色相の集計と仕分け
引数PixelsはBitmapSourceクラスのCopyPixelsで得られる色情報byte配列、今回もPixelFormatsはBgra32だけが対象
RGBから
HSVの変換にはMyHSV.DLLファイルを参照に追加したものを使っている、273行目
これをもとに切り抜き用のPathGeometryを作る
この画像を扇形(パイ型)に切り抜くための
PathGeometryを作成は
2019/4/3は昨日の
分割範囲ごとの扇形(パイ型)PathGeometryを作成して、全部を連結
さっきの色相リストをMakeClipに渡して作成
MyRadiusは切り抜く画像の半径
98行目、扇形(パイ型)PathGeometry作成のPieGeometryは昨日と全く同じ
連結は98行目PathGeometryクラスのAddGeometryメソッドを使ってできたので
2019/4/2はおとといのこれは必要なかった
扇形(パイ型)を連結したPathGeometryで切り抜きClipする
色相環画像作成は
2019/3/25は10日前の
この
色相環画像を表示したImageのClipに指定すれば完成になる
ギットハブ
こういうグラフの名前がわからないから棒グラフになっている
アプリダウンロード先(ヤフーボックス)
Release 20190402_色相円の棒グラフ · gogowaten/
WPF_test1
デザイン画面
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using MyHSV;
namespace _20190402_色相円の棒グラフ
{
public partial class MainWindow : Window
{
const double MyRadius = 200.0;
Point MyCenter = new Point(MyRadius, MyRadius);
byte[] MyPixels;
double[] MyHuesList;
int DivideCount = 120;
public MainWindow()
{
InitializeComponent();
this.AllowDrop = true;
Drop += MainWindow_Drop;
MyGrid.Children.Add(MakeAuxLine(MyCenter));
MyHueImage.Source = MakeHueBitmap((int)(MyRadius * 2));
MyHueImage.Clip = new RectangleGeometry(new Rect(0, 0, 0, 0));
}
#region イベント
private void RadioButton_Click(object sender, RoutedEventArgs e)
{
if (MyPixels == null) { return; }
RadioButton rb = sender as RadioButton;
int divCount = int.Parse((string)rb.Content);
DivideCount = divCount;
MyHueImage.Clip = MakeClip(HuePixelCount(MyHuesList, divCount));
}
private void MainWindow_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop) == false) { return; }
string[] filePath = (string[])e.Data.GetData(DataFormats.FileDrop);
var (pixels, bitmap) = MakeBitmapSourceAndByteArray(filePath[0], PixelFormats.Bgra32, 96, 96);
if (bitmap == null)
{
MessageBox.Show("画像ファイルじゃないみたい");
}
else
{
MyPixels = pixels;
MyHuesList = GetHueList(pixels);
var neko = HuePixelCount(MyHuesList, DivideCount);
MyHueImage.Clip = MakeClip(neko);
MyImage.Source = bitmap;
}
}
#endregion
private Geometry MakeClip(int[] hues)
{
double max = hues.Max();
Point center = new Point(MyRadius, MyRadius);
double divDeg = 360.0 / hues.Length;
double divdivDeg = divDeg / 2.0;
var clip = new PathGeometry();
clip.FillRule = FillRule.Nonzero;
for (int i = 0; i < hues.Length; i++)
{
var distance = hues[i] / max * MyRadius;
var centerDeg = i * divDeg;
var start = centerDeg - divdivDeg;
var stop = start + divDeg;
clip.AddGeometry(PieGeometry(center, distance, start, stop, SweepDirection.Clockwise));
}
return clip;
}
private Path MakeAuxLine(Point center)
{
var pg = new PathGeometry();
pg.AddGeometry(new EllipseGeometry(center, center.X, center.Y));
pg.AddGeometry(new EllipseGeometry(center, center.X * 3.0 / 4.0, center.Y * 3.0 / 4.0));
pg.AddGeometry(new EllipseGeometry(center, center.X / 2.0, center.Y / 2.0));
pg.AddGeometry(new EllipseGeometry(center, center.X / 4.0, center.Y / 4.0));
var p = new Path();
p.Stroke = Brushes.LightGray;
p.StrokeThickness = 1.0;
p.Opacity = 0.4;
p.Data = pg;
return p;
}
#region PathGeometry
<summary>
</summary>
<param name="center"></param>
<param name="distance"></param>
<param name="startDegrees"></param>
<param name="stopDegrees"></param>
<param name="direction"></param>
<returns></returns>
private PathGeometry PieGeometry(Point center, double distance, double startDegrees, double stopDegrees, SweepDirection direction)
{
Point start = MakePoint(startDegrees, center, distance);
Point stop = MakePoint(stopDegrees, center, distance);
double diffDegrees = (direction == SweepDirection.Clockwise) ? stopDegrees - startDegrees : startDegrees - stopDegrees;
if (diffDegrees < 0) { diffDegrees += 360.0; }
bool isLarge = (diffDegrees >= 180) ? true : false;
var arc = new ArcSegment(stop, new Size(distance, distance), 0, isLarge, direction, true);
var fig = new PathFigure();
fig.StartPoint = start;
fig.Segments.Add(arc);
fig.Segments.Add(new LineSegment(center, true));
fig.Segments.Add(new LineSegment(start, true));
fig.IsClosed = true;
var pg = new PathGeometry();
pg.Figures.Add(fig);
return pg;
}
<summary>
</summary>
<param name="degrees"></param>
<param name="center"></param>
<param name="distance"></param>
<returns></returns>
private Point MakePoint(double degrees, Point center, double distance)
{
if (degrees >= 360) { degrees = 359.99; }
var rad = Radian(degrees);
var cos = Math.Cos(rad);
var sin = Math.Sin(rad);
var x = center.X + cos * distance;
var y = center.Y + sin * distance;
return new Point(x, y);
}
private double Radian(double degree)
{
return Math.PI / 180.0 * degree;
}
#endregion
#region 色相環
<summary>
</summary>
<param name="size"></param>
<returns></returns>
private BitmapSource MakeHueBitmap(int size)
{
var wb = new WriteableBitmap(size, size, 96, 96, PixelFormats.Rgb24, null);
int stride = wb.BackBufferStride;
byte[] pixels = new byte[size * stride * 8];
double xDiff = size / 2.0;
double yDiff = size / 2.0;
int p = 0;
for (int y = 0; y < size; y++)
{
for (int x = 0; x < size; x++)
{
double radian = Math.Atan2(y - yDiff, x - xDiff);
double kakudo = Degrees(radian);
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;
}
}
wb.WritePixels(new Int32Rect(0, 0, size, size), pixels, stride, 0);
return wb;
}
public double Degrees(double radian)
{
double deg = radian / Math.PI * 180;
if (deg < 0) deg += 360;
return deg;
}
#endregion
#region 画像系
private double[] GetHueList(byte[] pixels)
{
double[] hueList = new double[pixels.Length / 4];
int count = 0;
for (int i = 0; i < pixels.Length; i += 4)
{
hueList[count] = HSV.Color2HSV(pixels[i + 2], pixels[i + 1], pixels[i]).Hue;
count++;
}
return hueList;
}
<summary>
=
<param name="hueList"></param>
<param name="divCount"></param>
<returns></returns>
private int[] HuePixelCount(double[] hueList, int divCount)
{
int[] table = new int[divCount];
double div = 360.0 / divCount;
double divdiv = div / 2.0;
for (int i = 0; i < hueList.Length; i++)
{
double hue = hueList[i];
if (hue == 360.0) { continue; }
hue = Math.Floor((hue + divdiv) / div);
hue = (hue >= divCount) ? 0 : hue;
table[(int)hue]++;
}
return table;
}
<summary>
</summary>
<param name="filePath"></param>
<param name="pixelFormat"></param>
<param name="dpiX"></param>
<param name="dpiY"></param>
<returns></returns>
private (byte[] array, BitmapSource source) MakeBitmapSourceAndByteArray(string filePath, PixelFormat pixelFormat, double dpiX = 0, double dpiY = 0)
{
byte[] pixels = null;
BitmapSource source = null;
try
{
using (System.IO.FileStream fs = new System.IO.FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
var bf = BitmapFrame.Create(fs);
var convertedBitmap = new FormatConvertedBitmap(bf, pixelFormat, null, 0);
int w = convertedBitmap.PixelWidth;
int h = convertedBitmap.PixelHeight;
int stride = (w * pixelFormat.BitsPerPixel + 7) / 8;
pixels = new byte[h * stride];
convertedBitmap.CopyPixels(pixels, stride, 0);
if (dpiX == 0) { dpiX = bf.DpiX; }
if (dpiY == 0) { dpiY = bf.DpiY; }
source = BitmapSource.Create(
w, h, dpiX, dpiY,
convertedBitmap.Format,
convertedBitmap.Palette, pixels, stride);
};
}
catch (Exception)
{
}
return (pixels, source);
}
#endregion
}
}