/// <summary>ラベルの包含関係を調べる</summary> /// <param name="label">ラベリングデータ</param> /// <returns>包含関係</returns> public int[][] GetInclusionLink(LabelStructure label) { logWriter.Write("ラベルの包含関係を調べます"); if (label == null) { logWriter.WriteError("ラベル情報がありません"); logWriter.WriteError("処理を中止します"); return(null); } int[][] link = new int[label.Max - label.Min + 1][]; using (PrograssWindow pw = new PrograssWindow("包含検査", label.Max - label.Min + 1)) { Parallel.For(label.Min, label.Max + 1, (n, state) => { link[n - label.Min] = GetInclusionNumber(label, n); pw.Add(); }); pw.Join(); } logWriter.Write("ラベルの包含関係を調べました"); return(link); }
/// <summary>ラベルの包含関係を調べる</summary> /// <param name="label">ラベリングデータ</param> /// <param name="token">キャンセルトークン</param> /// <returns>包含関係</returns> public int[][] GetInclusionLink(LabelStructure label, CancellationTokenSource token) { logWriter.Write("ラベルの包含関係を調べます"); if (label == null) { logWriter.WriteError("ラベル情報がありません"); logWriter.WriteError("処理を中止します"); return(null); } label.SetMinMax(); int[][] link = new int[label.Max - label.Min + 1][]; using (PrograssWindow pw = new PrograssWindow("包含検査", label.Max - label.Min + 1)) { Parallel.For(label.Min, label.Max + 1, (n, state) => { if (token.IsCancellationRequested) { state.Break(); } link[n - label.Min] = GetInclusionNumber3(label, n); pw.Add(); }); } logWriter.Write("ラベルの包含関係を調べました"); return(link); }
/// <summary>深さ情報を持った画像を保存する</summary> /// <param name="image">保存したい画像</param> /// <param name="depth">画像の深さデータ</param> /// <param name="path">保存したい場所</param> public void Save(Bitmap image, LabelStructure depth, string path) { logWriter.Write("三次元画像を保存します"); try { using (StreamWriter sw = new StreamWriter(path)) { sw.WriteLine(image.Width + " " + image.Height); for (int y = 0; y < image.Height; y++) { for (int x = 0; x < image.Width; x++) { Color c = image.GetPixel(x, y); sw.WriteLine(c.R + " " + c.G + " " + c.B + " " + depth[y, x]); } } } } catch (Exception ex) { logWriter.WriteError("三次元画像の保存に失敗しました"); logWriter.WriteError(ex.ToString()); return; } logWriter.Write("三次元画像を保存しました"); logWriter.Write("path=" + path); }
/// <summary>ラベル情報を視覚化する</summary> /// <param name="label">視覚化したいラベルデータ</param> /// <returns>視覚化された画像<see cref="Bitmap"/></returns> private Bitmap GetLabelImage(LabelStructure label) { logWriter.Write("ラベルデータの画像作成を行います"); if (label == null) { logWriter.WriteError("ラベルデータが存在しません"); logWriter.WriteError("画像作成を中止します"); return(null); } Bitmap bitmap = new Bitmap(label.Width, label.Height); label.SetMinMax(); int val = label.Max - label.Min + 1; int c = (byte.MaxValue + 1) / val; if (c == 0) { c = 16; } logWriter.Write("分割数 =" + val); logWriter.Write("色の変化量=" + c); { BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); byte[] buf = new byte[bitmap.Width * bitmap.Height * 4]; Marshal.Copy(data.Scan0, buf, 0, buf.Length); for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { int index = y * bitmap.Width * 4 + x * 4; int color = label[y, x] * c; buf[index + 0] = buf[index + 1] = buf[index + 2] = (byte)color; buf[index + 3] = byte.MaxValue; } } Marshal.Copy(buf, 0, data.Scan0, buf.Length); bitmap.UnlockBits(data); } logWriter.Write("ラベルデータの画像作成が完了しました"); return(bitmap); }
/// <summary> /// StartButton_Clickから呼び出される関数です /// </summary> private async Task ProcessAsync() { logWriter.Write("処理を開始します"); CancellationToken token = tokenSource.Token; Bitmap originalImage = await Task.Run(() => { return(new LoadImage(logWriter).Load(fileTextBox.Text)); }); token.ThrowIfCancellationRequested(); if (originalImage == null) { return; } new ImageWindow("元画像", originalImage, logWriter); Bitmap kmeansImage = await new K_means(8, logWriter).GetImageAsync(originalImage, tokenSource); token.ThrowIfCancellationRequested(); new ImageWindow("減色画像(k-means)", kmeansImage, logWriter); Bitmap medianImage = await new MedianFilter(logWriter).GetImageAsync(kmeansImage); token.ThrowIfCancellationRequested(); new ImageWindow("メディアンフィルタ", medianImage, logWriter); token.ThrowIfCancellationRequested(); LabelStructure label = await new Labeling(logWriter).GetLabelTableAsync(medianImage, tokenSource); token.ThrowIfCancellationRequested(); Bitmap labelImage = await new Labeling(logWriter).GetLabelImageAsync(label); new ImageWindow("ラベリング画像", labelImage, logWriter); LabelStructure depth = await new Guess01(logWriter).GetDepthAsync(label, tokenSource); token.ThrowIfCancellationRequested(); Bitmap depthImage = await new Labeling(logWriter).GetLabelImageAsync(depth); new ImageWindow("深さ情報", depthImage, logWriter); SaveImage(originalImage, depth); logWriter.Write("処理が完了しました"); }
/// <summary>深さ情報を持った画像のバイナリデータを保存する</summary> /// <param name="image">保存したい画像</param> /// <param name="depth">画像の深さデータ</param> /// <param name="path">保存したい場所</param> public void SaveBinary(Bitmap image, LabelStructure depth, string path) { /* * バイナリデータの保存形式 * 0_3byte 画像の横幅(int) * 4_7byte 画像の縦幅(int) * 8_byte 色・深さの情報 * 8n byte 赤色情報(byte) * 8n+1 byte 緑色情報(byte) * 8n+2 byte 青色情報(byte) * 8n+3 byte アルファ情報(byte) * 8n+4_8n+7byte 深さ情報(int) * 以下画像の大きさだけループ */ logWriter.Write("三次元画像を保存します"); try { using (BinaryWriter bw = new BinaryWriter(File.OpenWrite(path))) { bw.Write(image.Width); bw.Write(image.Height); for (int y = 0; y < image.Height; y++) { for (int x = 0; x < image.Width; x++) { Color c = image.GetPixel(x, y); bw.Write(c.R); bw.Write(c.G); bw.Write(c.B); bw.Write(c.A); bw.Write(depth[y, x]); } } } } catch (Exception ex) { logWriter.WriteError("三次元画像の保存に失敗しました"); logWriter.WriteError(ex.ToString()); return; } logWriter.Write("三次元画像を保存しました"); logWriter.Write("path=" + path); }
/// <summary>深さごとに画像を保存する</summary> /// <param name="image">保存したい画像</param> /// <param name="depth">画像の深さデータ</param> /// <param name="path">保存したい場所</param> public void SaveChip(Bitmap image, LabelStructure depth, string path) { logWriter.Write("画像を保存します"); try { for (int i = depth.Min; i <= depth.Max; i++) { Bitmap bitmap = new Bitmap(image); BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); byte[] buf = new byte[bitmap.Width * bitmap.Height * 4]; Marshal.Copy(data.Scan0, buf, 0, buf.Length); for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { if (depth[y, x] != i) { int index = y * bitmap.Width * 4 + x * 4; buf[index + 0] = 0; buf[index + 1] = 0; buf[index + 2] = 0; buf[index + 3] = 0; } } } Marshal.Copy(buf, 0, data.Scan0, buf.Length); bitmap.UnlockBits(data); string name = path + "_" + i.ToString() + ".png"; bitmap.Save(name); logWriter.Write("path=" + name); } } catch (Exception ex) { logWriter.WriteError("三次元画像の保存に失敗しました"); logWriter.WriteError(ex.ToString()); return; } logWriter.Write("画像を保存しました"); }
/// <summary>画像の深度を推測する</summary> /// <param name="label">ラベリング結果</param> /// <param name="token">キャンセルトークン</param> /// <returns>深度ラベル</returns> private LabelStructure GetDepth(LabelStructure label, CancellationTokenSource token) { logWriter.Write("深さ推測を行います"); if (label == null) { logWriter.WriteError("ラベルデータがありません"); logWriter.WriteError("深さ推測を中止します"); return(null); } int[][] link = new RingDetection(logWriter).GetInclusionLink(label, token); if (token.IsCancellationRequested) { return(null); } int[] depthTable = new int[link.Length]; for (int i = 0; i < link.Length; i++) { for (int j = 0; j < link[i].Length; j++) { depthTable[link[i][j]]++; } } LabelStructure depthGrid = new LabelStructure(label.Width, label.Height); for (int y = 0; y < label.Height; y++) { for (int x = 0; x < label.Width; x++) { int num = label[y, x]; depthGrid[y, x] = depthTable[num]; } } logWriter.Write("深さ推測を行いました"); return(depthGrid); }
/// <summary>画像の明るさを得る</summary> /// <param name="bmp">画像</param> /// <returns>光度ラベル</returns> public LabelStructure GetBrightness(Bitmap bmp) { LabelStructure label = new LabelStructure(bmp.Width, bmp.Height); BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); byte[] buf = new byte[bmp.Width * bmp.Height * 4]; Marshal.Copy(data.Scan0, buf, 0, buf.Length); bmp.UnlockBits(data); for (int i = 0; i < buf.Length; i += 4) { HSV hsv = HSV.FromRGB(buf[i + 0], buf[i + 1], buf[i + 2]); int x = i / 4 % bmp.Width; int y = i / 4 / bmp.Width; label[y, x] = (int)(hsv.V * 255); } return(label); }
/// <summary>画像を保存します</summary> /// <param name="image">画像</param> /// <param name="depth">深さ</param> private void SaveImage(Bitmap image, LabelStructure depth) { DialogResult com = MessageBox.Show("3次元画像を保存しますか?", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (com == DialogResult.Yes) { SaveFileDialog saveDialog = new SaveFileDialog(); saveDialog.Filter = "3次元画像|*.rgbad;*.txt"; saveDialog.Title = "保存"; saveDialog.DefaultExt = "rgbad"; if (saveDialog.ShowDialog() == DialogResult.OK) { string path = Path.GetDirectoryName(saveDialog.FileName) + "\\" + Path.GetFileNameWithoutExtension(saveDialog.FileName); SaveImage saveImage = new SaveImage(logWriter); saveImage.Save(image, depth, path + ".txt"); saveImage.SaveBinary(image, depth, path + ".rgbad"); depth.SetMinMax(); saveImage.SaveChip(image, depth, path); } } }
/// <summary>画像の深度を推測する</summary> /// <param name="label">ラベリング結果</param> /// <returns>深度ラベル</returns> public LabelStructure GetDepth(LabelStructure label) { logWriter.Write("深さ推測を行います"); if (label == null) { logWriter.WriteError("ラベルデータがありません"); logWriter.WriteError("深さ推測を中止します"); return(null); } int[][] link = new RingDetection(logWriter).GetInclusionLink(label); int[] depthTable = new int[link.Length]; for (int i = 0; i < link.Length; i++) { for (int j = 0; j < link[i].Length; j++) { depthTable[link[i][j]]++; } } LabelStructure depthGrid = new LabelStructure(new int[label.Height, label.Width]); for (int y = 0; y < label.Height; y++) { for (int x = 0; x < label.Width; x++) { int num = label[y, x]; depthGrid[y, x] = depthTable[num]; } } logWriter.Write("深さ推測を行いました"); return(depthGrid); }
/// <summary>画像の深度を推測する(非同期)</summary> /// <param name="label">ラベリング結果</param> /// <param name="token">キャンセルトークン</param> /// <returns>深度ラベル</returns> public async Task <LabelStructure> GetDepthAsync(LabelStructure label, CancellationTokenSource token) { return(await Task.Run(() => GetDepth(label, token))); }
//遅い・不正確 /// <summary>特定のラベルの包含関係を調べる</summary> /// <param name="label">ラベリングデータ</param> /// <param name="n">ラベル番号</param> /// <returns>包含関係</returns> private int[] GetInclusionNumber(LabelStructure label, int n) { int[] dx = new int[] { -1, 0, 1, -1 }; int[] dy = new int[] { -1, -1, -1, 0 }; Dictionary <int, bool> table = new Dictionary <int, bool>(); for (int y = 0; y < label.Height; y++) { for (int x = 0; x < label.Width; x++) { if (!table.ContainsKey(label[y, x])) { table[label[y, x]] = false; } if (x - 1 < 0 || label.Width <= x + 1 || y - 1 < 0 || label.Height <= y + 1) { table[label[y, x]] = true; for (int i = 0; i < 4; i++) { if (0 <= x + dx[i] && x + dx[i] < label.Width && 0 <= y + dy[i]) { if (label[y + dy[i], x + dx[i]] != n) { table[label[y + dy[i], x + dx[i]]] = true; } } } } else { for (int i = 0; i < 4; i++) { if (0 <= x + dx[i] && x + dx[i] < label.Width && 0 <= y + dy[i]) { if (label[y + dy[i], x + dx[i]] != n) { int l = label[y + dy[i], x + dx[i]]; if (table[l]) { table[label[y, x]] = true; } } } } } } } HashSet <int> inclusion = new HashSet <int>(); for (int y = 0; y < label.Height; y++) { for (int x = 0; x < label.Width; x++) { if (label[y, x] != n && !table[label[y, x]]) { inclusion.Add(label[y, x]); } } } return(inclusion.ToArray()); }
//早い・不正確 /// <summary>特定のラベルの包含関係を調べる</summary> /// <param name="label">ラベリングデータ</param> /// <param name="n">ラベル番号</param> /// <returns>包含関係</returns> private int[] GetInclusionNumber2(LabelStructure label, int n) { Dictionary <int, bool> table = new Dictionary <int, bool>(); for (int i = label.Min; i <= label.Max; i++) { table[i] = false; } for (int x = 0; x < label.Width; x++) { if (label[0, x] != n) { table[label[0, x]] = true; } if (label[label.Height - 1, x] != n) { table[label[label.Height - 1, x]] = true; } } for (int y = 0; y < label.Height; y++) { if (label[y, 0] != n) { table[label[y, 0]] = true; } if (label[y, label.Width - 1] != n) { table[label[y, label.Width - 1]] = true; } } for (int y = 1; y < label.Height - 1; y++) { for (int x = 1; x < label.Width - 1; x++) { if (label[y, x] != n && !table[label[y, x]]) { foreach (var d in Config.Direction) { int num = label[y + d.Y, x + d.X]; if (table[num]) { table[label[y, x]] = true; break; } } } } } List <int> list = new List <int>(); table[n] = true; foreach (var val in table) { if (!val.Value) { list.Add(val.Key); } } return(list.ToArray()); }
//遅い・正確 /// <summary>特定のラベルの包含関係を調べる</summary> /// <param name="label">ラベリングデータ</param> /// <param name="n">ラベル番号</param> /// <returns>包含関係</returns> private int[] GetInclusionNumber3(LabelStructure label, int n) { int[] table = new int[label.Max - label.Min + 1]; for (int i = 0; i < table.Length; i++) { table[i] = i; } for (int y = 0; y < label.Height; y++) { for (int x = 0; x < label.Width; x++) { if (label[y, x] != n) { int min = int.MaxValue; foreach (var d in Config.Direction) { if (0 <= x + d.X && x + d.X < label.Width && 0 <= y + d.Y && y + d.Y < label.Height) { if (label[y + d.Y, x + d.X] != n) { min = Math.Min(min, table[label[y + d.Y, x + d.X]]); } } } if (table[label[y, x]] > min) { for (int i = 0; i < table.Length; i++) { if (table[i] == table[label[y, x]]) { table[i] = min; } } for (int i = 0; i < table.Length; i++) { while (table[i] != table[table[i]]) { table[i] = table[table[i]]; } } } } } } for (int x = 0; x < label.Width; x++) { table[label[0, x]] = 0; table[label[label.Height - 1, x]] = 0; } for (int y = 0; y < label.Height; y++) { table[label[y, 0]] = 0; table[label[y, label.Width - 1]] = 0; } for (int i = 0; i < table.Length; i++) { while (table[i] != table[table[i]]) { table[i] = table[table[i]]; } } List <int> list = new List <int>(); for (int i = 0; i < table.Length; i++) { if (table[i] != 0 && i != n) { list.Add(i); } } return(list.ToArray()); }
/// <summary>ラベル情報を視覚化する(非同期)</summary> /// <param name="label">視覚化したいラベルデータ</param> /// <returns>視覚化された画像<see cref="Bitmap"/></returns> public async Task <Bitmap> GetLabelImageAsync(LabelStructure label) { return(await Task.Run(() => GetLabelImage(label))); }
/// <summary>画像からラベルを作成する</summary> /// <param name="bmp">ラベルを作成したい画像</param> /// <param name="token">中断用構造体</param> /// <returns>ラベル情報<see cref="LabelStructure" /></returns> private LabelStructure GetLabelTable(Bitmap bmp, CancellationTokenSource token) { logWriter.Write("ラベリング処理を行います"); if (bmp == null) { logWriter.WriteError("画像が存在しません"); logWriter.WriteError("ラベリング処理を中止します"); return(null); } LabelStructure label = new LabelStructure(bmp.Width, bmp.Height); #region カラー配列の作成 BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); byte[] buf = new byte[bmp.Width * bmp.Height * 4]; Marshal.Copy(data.Scan0, buf, 0, buf.Length); bmp.UnlockBits(data); #endregion Func <int, int, int> ToIndex = (x, y) => { return(y * bmp.Width * 4 + x * 4); }; #region 番号振り分け Func <int, int, int> SearchLabel = (x, y) => { int max = 0; int index1 = ToIndex(x, y); foreach (var d in Config.Direction) { int index2 = ToIndex(x + d.X, y + d.Y); if (0 <= x + d.X && x + d.X < label.Width && 0 <= y + d.Y && y + d.Y < label.Height) { if (buf[index1 + 0] == buf[index2 + 0] && buf[index1 + 1] == buf[index2 + 1] && buf[index1 + 2] == buf[index2 + 2]) { max = Math.Max(max, label[y + d.Y, x + d.X]); } } } return(max); }; int count = 0; for (int y = 0; y < label.Height; y++) { for (int x = 0; x < label.Width; x++) { int num = SearchLabel(x, y); if (num == 0) { label[y, x] = count + 1; count++; } else { label[y, x] = num; } } } #endregion #region 番号テーブルの作成 int[] table = new int[count + 1]; for (int i = 0; i < table.Length; i++) { table[i] = i; } Func <int, int, int> SearchTable = (x, y) => { int max = 0; int index1 = ToIndex(x, y); foreach (var d in Config.Direction) { int index2 = ToIndex(x + d.X, y + d.Y); if (0 <= x + d.X && x + d.X < label.Width && 0 <= y + d.Y && y + d.Y < label.Height) { if (buf[index1 + 0] == buf[index2 + 0] && buf[index1 + 1] == buf[index2 + 1] && buf[index1 + 2] == buf[index2 + 2]) { max = Math.Max(max, table[label[y + d.Y, x + d.X]]); } } } return(max); }; for (int y = 0; y < label.Height; y++) { for (int x = 0; x < label.Width; x++) { int num = SearchTable(x, y); if (num != 0) { if (num > table[label[y, x]]) { for (int i = 0; i < table.Length; i++) { if (table[i] == num) { table[i] = label[y, x]; } } for (int i = 0; i < table.Length; i++) { while (table[i] != table[table[i]]) { table[i] = table[table[i]]; } } } } } } #endregion #region 番号の最適化 int newCount = 0; for (int i = 0; i < table.Length; i++) { if (newCount < table[i]) { int n = table[i]; for (int j = 0; j < table.Length; j++) { if (table[j] == n) { table[j] = newCount; } } newCount++; } } for (int y = 0; y < label.Height; y++) { for (int x = 0; x < label.Width; x++) { label[y, x] = table[label[y, x]]; } } #endregion logWriter.Write("ラベリング処理が完了しました"); return(label); }