画像判断基準にするものは、4分木した画像の平均と偏差2つを見て判断します。
public class ClusterCollection : List<ClusterModel>
{
public int Id { get; set; }
/// <summary>
/// モデル化
/// </summary>
/// <returns></returns>
public ClusterModel ToModel()
{
if (1 < Count)
{
return new ClusterModel(ToArray());
}
else
{
return this[0];
}
}
public ClusterCollection(int id)
{
Id = id;
}
}
public class ClusterGroup
{
public List<ClusterCollection> Groups { get; } = new List<ClusterCollection>();
/// <summary>
/// クラスタ初期状態を作成
/// </summary>
/// <param ="collection"></param>
public ClusterGroup(ClusterModel[] models)
{
var id = 1;
foreach (var model in models)
{
Groups.Add(new ClusterCollection(id++) { model });
}
}
/// <summary>
/// クラスタリング作成
/// </summary>
public void Clustering()
{
//リンク作成
var _links = new ClusterLinkCollection();
foreach (var model1 in Groups)
{
var __links = new ClusterLinkCollection(Groups, model1);
var model = __links.GetAnswer();
_links.Add(model);
}
var distances = _links.Select(_ => _.Distance).ToArray();
var dlen = distances.Length;
var dmax = distances.Max();
var dave = distances.Average();
var dthr = (dmax + dave) / 2.0;
var links = new ClusterLinkCollection();
links.AddRange(_links.Where(_ => dave <= _.Distance).ToArray());
//合体
foreach (var link in links)
{
var numlist = Groups.Select(_ => _.Id).ToList();
if (!numlist.Contains(link.Index1.Id)) continue;
if (!numlist.Contains(link.Index2.Id)) continue;
link.Index1.AddRange(link.Index2);
Groups.Remove(link.Index2);
}
}
/// <summary>
/// 一番最もらしいグループを抽出
/// </summary>
/// <returns></returns>
public ClusterCollection GetMostPopulure()
{
var max = Groups.Select(_ => _.Count).Max();
var collection = Groups.Where(_ => _.Count == max).First();
return collection;
}
}
/// <summary>
/// クラスタコレクション
/// </summary>
public class ClusterLinkCollection : List<ClusterLinkModel>
{
public ClusterLinkCollection()
{
}
public ClusterLinkCollection(List<ClusterCollection> collection, ClusterCollection model)
{
foreach (var _model in collection)
{
Add(new ClusterLinkModel(_model, model));
}
}
/// <summary>
/// 一番正解率の高いリンクを返す
/// </summary>
/// <returns></returns>
public ClusterLinkModel GetAnswer()
{
var max = this.Select(_ => _.Distance).Max();
return this.Where(_ => _.Distance == max).First();
}
/// <summary>
/// コレクションのカウント数を数える
/// </summary>
/// <param name="collection"></param>
/// <returns></returns>
public int GetCount(ClusterCollection collection)
{
var count = this.Where(_ => _.Index1 == collection || _.Index2 == collection).Count();
return count;
}
/// <summary>
/// 関連データ一覧を取得する
/// </summary>
/// <param name="collection"></param>
/// <returns></returns>
public List<ClusterCollection> ContainData(ClusterCollection collection)
{
var result = new List<ClusterCollection>();
foreach (var link in this)
{
if (link.Index1 == collection)
{
if (!result.Contains(link.Index2))
{
result.Add(link.Index2);
}
}
else if (link.Index2 == collection)
{
if (!result.Contains(link.Index1))
{
result.Add(link.Index1);
}
}
}
return result;
}
}
/// <summary>
/// クラスタモデル
/// </summary>
public class ClusterLinkModel
{
public ClusterCollection Index1 { get; }
public ClusterCollection Index2 { get; }
public double Distance { get; }
/// <summary>
/// コンストラクタ処理
/// </summary>
/// <param name="model1"></param>
/// <param name="model2"></param>
public ClusterLinkModel(ClusterCollection model1, ClusterCollection model2)
{
Index1 = model1;
Index2 = model2;
var m1 = model1.ToModel();
var m2 = model2.ToModel();
Distance = (model1 == model2) ? 0.0 : m1.Matching(m2);
}
}
/// <summary>
/// モデルクラス
/// パターンデータで一致率を返す
/// </summary>
public class ClusterModel
{
public double[] Data { get; }
public double[] Patterns { get; }
public int Count => Patterns.Length;
const int split_count = 8;
public int KeisokuX { get; }
public LRGB Color { get; set; }
public int Touka { get; set; }
public bool Empty { get; set; }
public bool Stable { get; set; }
public int Saturation { get; set; }
public Bitmap ImageIn => Data.DrawImage();
/// <summary>
/// コンストラクタ処理
/// </summary>
/// <param name="x"></param>
/// <param name="data"></param>
/// <param name="bmp"></param>
/// <param name="model"></param>
public ClusterModel(int x, double[] data, Bitmap bmp, AnalyzeModel model)
{
KeisokuX = model.AnalyzeArea.X + x;
Data = data.Gaussian();
var ave = Data.Average();
var dat1 = Data.Where(_ => ave < _).ToList();
dat1.Sort();
var len = dat1.Count;
dat1.RemoveRange(0, (int)(len * 0.5));
Data.Normalize();
//パターン作成
var alen = data.Length;
var spt = (alen / split_count);
Patterns = new double[split_count * 2];
for (var idx = 0; idx < split_count; idx++)
{
Patterns[idx * 2 + 0] = data.ToList().GetRange(idx * spt, spt).ToArray().Average();
Patterns[idx * 2 + 1] = data.ToList().GetRange(idx * spt, spt).ToArray().Sigma();
}
//解析
Analyze(KeisokuX, bmp, model);
}
/// <summary>
/// 解析処理
/// </summary>
/// <param name="bmp"></param>
/// <param name="model"></param>
void Analyze(int ix, Bitmap bmp, AnalyzeModel model)
{
var len = Data.Length;
var gain = model.Gain;
var sum = 0.0;
var weight = len * gain;
var r = 0;
var g = 0;
var b = 0;
var cnt = 0;
var iy = model.AnalyzeArea.Y;
var min = 99999999.0;
var max = 0.0;
var old = Data[0];
var bibun = Data.Bibun();
var henbibun = bibun.Bibun();
var ave = Data.Average();
var wcount = 0.0;
var dcount = 0.0;
for (var idx = 2; idx < len - 2; idx++)
{
var dat = Data[idx];
var col = bmp.GetPixel(ix, iy);
//赤色判定個所をカウント
if (ave <= dat)
{
//山の部分はRGB色
r += col.R;
g += col.G;
b += col.B;
cnt++;
wcount += dat;
}
else
{
dcount += dat;
}
//黒→白へ変化している値をカウント(透過度)
var tmp = Math.Abs(dat - old);
sum += tmp * weight;
//彩度の計算
double _min = Math.Min(col.R, Math.Min(col.G, col.B));
double _max = Math.Max(col.R, Math.Max(col.G, col.B));
var _val = (0 == _max) ? 1 : ((_max - _min) / _max);
min = Math.Min(min, _val);
max = Math.Max(max, _val);
old = dat;
weight -= gain;
iy++;
}
r /= cnt;
g /= cnt;
b /= cnt;
Color = new LRGB(r, g, b); //RGB値
Touka = (int)(sum * 10); //透過度
Saturation = (0.0 == max) ? 0 : (int)((1 - ((max - min) / (min + max))) * 100); //彩度
Stable = Data[0] > Data[len - 1]; //上が明るくて下が暗い
Empty = (model.Threshold0 >= Saturation); //チューブ有無
}
/// <summary>
/// コンストラクタ処理
/// </summary>
/// <param name="models"></param>
/// <param name="split_count"></param>
public ClusterModel(ClusterModel[] models)
{
Data = models.Select(_ => _.Data).ToArray().Synthesis();
var len = Data.Length;
var spt = (len / split_count);
Patterns = new double[split_count * 2];
for (var idx = 0; idx < split_count; idx++)
{
Patterns[idx * 2 + 0] = Data.ToList().GetRange(idx * spt, spt).ToArray().Average();
Patterns[idx * 2 + 1] = Data.ToList().GetRange(idx * spt, spt).ToArray().Sigma();
}
}
/// <summary>
/// コサイン類似度
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public double Matching(ClusterModel model)
{
var sum = 0.0;
var v1 = 0.0;
var v2 = 0.0;
for (var stage = 0; stage < Count; stage++)
{
var val1 = Patterns[stage];
var val2 = model.Patterns[stage];
sum += val1 * val2;
v1 += Math.Pow(val1, 2);
v2 += Math.Pow(val2, 2);
}
var result = sum / Math.Sqrt(v1 * v2);
return result;
}
}
0 件のコメント:
コメントを投稿