diff --git a/TIASshot/Config.cs b/TIASshot/Config.cs index bbbdd92..6a79cc4 100644 --- a/TIASshot/Config.cs +++ b/TIASshot/Config.cs @@ -61,5 +61,15 @@ } return value; } + + /// + /// 設定ファイルから文字列を取得 + /// + /// + /// + public static string GetString(string param) { + XmlNode node = doc.SelectSingleNode($"//Config/{param}"); + return node?.InnerText ?? ""; + } } } diff --git a/TIASshot/Form1.Designer.cs b/TIASshot/Form1.Designer.cs index 8218abe..f48df48 100644 --- a/TIASshot/Form1.Designer.cs +++ b/TIASshot/Form1.Designer.cs @@ -32,8 +32,9 @@ this.picDisplay = new System.Windows.Forms.PictureBox(); this.btnShotMulti = new System.Windows.Forms.Button(); this.btnCalib = new System.Windows.Forms.Button(); - this.btnCheck = new System.Windows.Forms.Button(); this.txtMessage = new System.Windows.Forms.TextBox(); + this.txtSaveFolder = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); ((System.ComponentModel.ISupportInitialize)(this.picPreview)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.picDisplay)).BeginInit(); this.SuspendLayout(); @@ -88,9 +89,9 @@ // // picPreview // - this.picPreview.Location = new System.Drawing.Point(15, 377); + this.picPreview.Location = new System.Drawing.Point(12, 565); this.picPreview.Name = "picPreview"; - this.picPreview.Size = new System.Drawing.Size(108, 57); + this.picPreview.Size = new System.Drawing.Size(79, 57); this.picPreview.TabIndex = 4; this.picPreview.TabStop = false; this.picPreview.Visible = false; @@ -102,7 +103,7 @@ | System.Windows.Forms.AnchorStyles.Right))); this.picDisplay.Location = new System.Drawing.Point(292, 6); this.picDisplay.Name = "picDisplay"; - this.picDisplay.Size = new System.Drawing.Size(340, 428); + this.picDisplay.Size = new System.Drawing.Size(449, 610); this.picDisplay.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; this.picDisplay.TabIndex = 5; this.picDisplay.TabStop = false; @@ -121,27 +122,15 @@ // btnCalib // this.btnCalib.AllowDrop = true; - this.btnCalib.Location = new System.Drawing.Point(15, 324); + this.btnCalib.Location = new System.Drawing.Point(100, 579); this.btnCalib.Name = "btnCalib"; - this.btnCalib.Size = new System.Drawing.Size(111, 37); + this.btnCalib.Size = new System.Drawing.Size(88, 37); this.btnCalib.TabIndex = 7; this.btnCalib.Text = "校正"; this.btnCalib.UseVisualStyleBackColor = true; this.btnCalib.Visible = false; this.btnCalib.Click += new System.EventHandler(this.btnCalib_Click); // - // btnCheck - // - this.btnCheck.AllowDrop = true; - this.btnCheck.Location = new System.Drawing.Point(144, 324); - this.btnCheck.Name = "btnCheck"; - this.btnCheck.Size = new System.Drawing.Size(111, 37); - this.btnCheck.TabIndex = 8; - this.btnCheck.Text = "チェック"; - this.btnCheck.UseVisualStyleBackColor = true; - this.btnCheck.Visible = false; - this.btnCheck.Click += new System.EventHandler(this.btnCheck_Click); - // // txtMessage // this.txtMessage.BackColor = System.Drawing.Color.LemonChiffon; @@ -153,13 +142,33 @@ this.txtMessage.TabIndex = 9; this.txtMessage.Text = "舌診チャートを設置してください."; // + // txtSaveFolder + // + this.txtSaveFolder.Font = new System.Drawing.Font("MS UI Gothic", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128))); + this.txtSaveFolder.Location = new System.Drawing.Point(15, 459); + this.txtSaveFolder.Name = "txtSaveFolder"; + this.txtSaveFolder.ReadOnly = true; + this.txtSaveFolder.Size = new System.Drawing.Size(268, 23); + this.txtSaveFolder.TabIndex = 10; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("MS UI Gothic", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128))); + this.label3.Location = new System.Drawing.Point(12, 440); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(123, 16); + this.label3.TabIndex = 11; + this.label3.Text = "データ保存フォルダ"; + // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(644, 452); + this.ClientSize = new System.Drawing.Size(753, 634); + this.Controls.Add(this.label3); + this.Controls.Add(this.txtSaveFolder); this.Controls.Add(this.txtMessage); - this.Controls.Add(this.btnCheck); this.Controls.Add(this.btnCalib); this.Controls.Add(this.btnShotMulti); this.Controls.Add(this.picDisplay); @@ -191,8 +200,9 @@ private System.Windows.Forms.PictureBox picDisplay; private System.Windows.Forms.Button btnShotMulti; private System.Windows.Forms.Button btnCalib; - private System.Windows.Forms.Button btnCheck; private System.Windows.Forms.TextBox txtMessage; + private System.Windows.Forms.TextBox txtSaveFolder; + private System.Windows.Forms.Label label3; } } diff --git a/TIASshot/Form1.cs b/TIASshot/Form1.cs index dc69258..d4d0d8a 100644 --- a/TIASshot/Form1.cs +++ b/TIASshot/Form1.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Interop; +using System.Xml.Serialization; namespace TIASshot { public partial class Form1 : Form { @@ -45,6 +46,7 @@ } txtDeviceName.Text = _lucam.DeviceName; txtSerialNo.Text = _lucam.SerialNumber; + txtSaveFolder.Text = Config.GetString("SaveFolder"); EnableShots(false); _lucam.StartStopPreview(); } @@ -86,15 +88,6 @@ } /// - /// 動作チェックボタン - /// - /// - /// - private void btnCheck_Click(object sender, EventArgs e) { - _lucam.Check(); - } - - /// /// 画像表示 /// /// diff --git a/TIASshot/Lucam.cs b/TIASshot/Lucam.cs index 985946e..3ad9af8 100644 --- a/TIASshot/Lucam.cs +++ b/TIASshot/Lucam.cs @@ -16,6 +16,8 @@ using System.Configuration; using System.Web.ModelBinding; using System.Windows.Controls; +using System.IO; +using static System.Resources.ResXFileRef; namespace TIASshot { @@ -29,20 +31,21 @@ public string ErrorMsg { get; private set; } readonly Dictionary ARDict = CvAruco.GetPredefinedDictionary(PredefinedDictionaryName.Dict4X4_50); - readonly float RefRGB; - readonly float UpdateRate; readonly Point2f[] PointsDst40 = new Point2f[] { new Point2f(345, 130),new Point2f(465, 130),new Point2f(465, 250),new Point2f(345, 250), }; readonly Point2f[] PointsDst41 = new Point2f[]{ new Point2f(345, 1200), new Point2f(465, 1200), new Point2f(465, 1320), new Point2f(345, 1320), }; + readonly float RefRGB; + readonly float UpdateRate; + readonly Mat TCC_SRGB; IntPtr _hCam = IntPtr.Zero; PictureBox _picPreview, _picDisplay; Form1 _form; bool _isPreview = false; - bool _check = false; + //bool _check = false; int _calibrating = 0; bool _calibrated = false; dll.LucamSnapshot _snap; @@ -54,6 +57,7 @@ List _chartMasks = new List(); int _detectionCount = 0; Point2f _lastPosition = new Point2f(0, 0); + Mat _convRGB2SRGB; /// /// コンストラクタ @@ -69,6 +73,8 @@ RefRGB = Config.GetFloat("ReferenceValue"); UpdateRate = Config.GetFloat("UpdateRate"); + TCC_SRGB = LoadMatFromCsv(Config.GetString("TccTableCsvSrgb")); + // カメラパラメータの初期値 _snap.BufferLastFrame = false; _snap.Exposure = Config.GetFloat("Exposure"); @@ -111,6 +117,10 @@ ErrorMsg = "設定ファイル(Config.xml)の読み込みに失敗しました.\r\n終了します."; return false; } + if (TCC_SRGB is null) { + ErrorMsg = $"ファイル({Config.GetString("TccTableCsvSrgb")})の読み込みに失敗しました.\r\n終了します."; + return false; + } var numCam = dll.LucamNumCameras(); if ( numCam < 1 ) { @@ -274,6 +284,7 @@ } _calibrating--; if (_calibrating == 0) { + _convRGB2SRGB = CalcConvertMatrix(imgt, TCC_SRGB); _form.ShowMessage("自動校正完了"); _form.EnableShots(); _calibrated = true; @@ -291,6 +302,44 @@ } /// + /// 変換行列算出 + /// + /// 画像 + /// 変換目標の24x3行列 + private Mat CalcConvertMatrix(Mat img, Mat target) { + // 画像からチャートのRGB値算出 + var arrRGB = new Mat(24, 3, MatType.CV_64FC1); + for (int i = 0; i < _chartMasks.Count; i++) { + var rgb = Cv2.Mean(img, _chartMasks[i]); + arrRGB.At(i, 0) = rgb.Val0; + arrRGB.At(i, 1) = rgb.Val1; + arrRGB.At(i, 2) = rgb.Val2; + } + var extended = ExtendMat(arrRGB); + //Debug.WriteLine("extended"); + //Debug.WriteLine(extended.Dump()); + + // 変換行列算出 + Mat convMat = new Mat(17, 3, MatType.CV_64FC1); + Cv2.Solve(extended, target, convMat, DecompTypes.SVD); + //Debug.WriteLine("convMat"); + //Debug.WriteLine(convMat.Dump()); + + // 変換精度検証 + var converted = extended * convMat; + //Debug.WriteLine("converted"); + //Debug.WriteLine(converted.ToMat().Dump()); + + //Mat impSRGB = converted.ToMat().Reshape(3); + //Cv2.ImWrite("impSRGB.png", impSRGB); + + var diff = Math.Sqrt(Cv2.Norm(target, converted, NormTypes.L2)); + Debug.WriteLine($"変換行列の誤差 = {diff:.000}"); + + return convMat; + } + + /// /// 画像撮影1枚 /// public void ShotOne() { @@ -318,16 +367,20 @@ for (var i = 0; i < numImages; i++) { var ret = dll.LucamTakeFastFrame(_hCam, rawImage); //Debug.WriteLine(ret); - if (i < numImages - 1) Thread.Sleep(interval); dll.LucamConvertFrameToRgb24(_hCam, rgbImage, rawImage, _snap.Format.Width, _snap.Format.Height, dll.LucamPixelFormat.PF_8, ref _convert); using (Mat img = Mat.FromPixelData(_snap.Format.Height, _snap.Format.Width, MatType.CV_8UC3, rgbImage)) { //Cv2.ImWrite($"orig_{i:00}.jpg", img); using (Mat imgt = img.T()) { - Cv2.ImWrite($"snap_{i:00}.jpg", imgt); + Cv2.ImWrite($"conv0_{i:00}.jpg", imgt); + using (var converted = ConvertImage(imgt, _convRGB2SRGB)) { + Cv2.ImWrite($"conv1_{i:00}.jpg", converted); + } } } + + if (i < numImages - 1) Thread.Sleep(interval); } rgbImage = null; rawImage = null; @@ -373,10 +426,85 @@ } /// - /// 動作チェック有効化 + /// csvファイルからMatを読み込む /// - public void Check() { - _check = true; + /// + /// + /// + private Mat LoadMatFromCsv(string csvFile) { + try { + var arr = new List(); + int cols = -1; + using (var reader = new StreamReader(csvFile)) { + while (!reader.EndOfStream) { + var line = reader.ReadLine(); + var valStrs = line.Split(','); + if (cols == -1) cols = valStrs.Length; + else if (cols != valStrs.Length) throw new Exception("cols != valStrs.Length"); + var vals = valStrs.Select(x => double.Parse(x)).ToArray(); + arr.Add(vals); + } + } + var m = new Mat(arr.Count, cols, MatType.CV_64FC1); + for (var row = 0; row < arr.Count; row++) { + for (var col = 0; col < cols; col++) { + m.At(row, col) = arr[row][col]; + } + } + return m; + } catch (Exception) { + return null; + } + } + + /// + /// 画像の色変換 + /// + /// + /// + /// double型画像 + private Mat ConvertImage(Mat src, Mat conv) { + if (src.Type() != MatType.CV_64FC3) { + src.ConvertTo(src, MatType.CV_64FC3); + } + var flatten = src.Reshape(3, src.Height * src.Width); + var extended = ExtendMat(flatten); + var converted = (extended * conv).ToMat(); + var convertedImage = converted.Reshape(3, src.Height); + return convertedImage; + } + + /// + /// 行列の拡張 3次元→17次元 + /// + /// + /// + private Mat ExtendMat(Mat src) { + if (src.Cols * src.Channels() != 3) return src; + var dst = new Mat(src.Rows, 17, MatType.CV_64FC1); + for (int row = 0; row < src.Rows; row++) { + var b = src.Cols == 1 ? src.At(row, 0)[0] : src.At(row, 0); + var g = src.Cols == 1 ? src.At(row, 0)[1] : src.At(row, 1); + var r = src.Cols == 1 ? src.At(row, 0)[2] : src.At(row, 2); + dst.At(row, 0) = r; + dst.At(row, 1) = g; + dst.At(row, 2) = b; + dst.At(row, 3) = r * g; + dst.At(row, 4) = r * b; + dst.At(row, 5) = g * b; + dst.At(row, 6) = r * r; + dst.At(row, 7) = g * g; + dst.At(row, 8) = b * b; + dst.At(row, 9) = r * r * b; + dst.At(row, 10) = r * r * g; + dst.At(row, 11) = g * g * r; + dst.At(row, 12) = g * g * b; + dst.At(row, 13) = b * b * r; + dst.At(row, 14) = b * b * g; + dst.At(row, 15) = r * g * b; + dst.At(row, 16) = 1.0; + } + return dst; } /// diff --git a/TIASshot/TIASshot.csproj b/TIASshot/TIASshot.csproj index 44f81fa..d1e6c2f 100644 --- a/TIASshot/TIASshot.csproj +++ b/TIASshot/TIASshot.csproj @@ -153,6 +153,8 @@ - copy /Y $(ProjectDir)config.xml $(TargetDir) + copy /Y $(ProjectDir)config.xml $(TargetDir) +copy /Y $(ProjectDir)tcc_srgb.csv $(TargetDir) + \ No newline at end of file diff --git a/TIASshot/config.xml b/TIASshot/config.xml index 3b5432f..2c4d0f7 100644 --- a/TIASshot/config.xml +++ b/TIASshot/config.xml @@ -5,8 +5,8 @@ 2.62 1.72 1.70 - 230 - 230 + 220 + 220 200 0.5 50 @@ -15,4 +15,6 @@ 1000 4.0 30 + tcc_srgb.csv + C:\TIAS_Data diff --git a/TIASshot/tcc_srgb.csv b/TIASshot/tcc_srgb.csv new file mode 100644 index 0000000..67d9f83 --- /dev/null +++ b/TIASshot/tcc_srgb.csv @@ -0,0 +1,24 @@ +92,110,181 +77,77,164 +61,57,146 +60,56,130 +98,95,176 +105,95,153 +69,63,105 +136,133,156 +80,77,97 +106,110,164 +131,153,176 +89,121,168 +226,226,226 +144,144,144 +118,118,118 +94,94,94 +70,70,70 +48,48,48 +147,103,66 +73,157,90 +71,77,184 +93,199,205 +150,108,189 +192,162,77