using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Lumenera.USB;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System.Drawing;
using System.Windows.Interop;
using System.Threading;
using OpenCvSharp.Aruco;
using System.Windows.Media.Animation;
using System.Configuration;
using System.Web.ModelBinding;
using System.Windows.Controls;
namespace TIASshot {
/// <summary>
/// Lumeneraカメラクラス
/// </summary>
internal class Lucam {
public string DeviceName { get; private set; } = "Unknown";
public string SerialNumber { get; private set; }
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),
};
IntPtr _hCam = IntPtr.Zero;
PictureBox _picPreview, _picDisplay;
Form1 _form;
bool _isPreview = false;
bool _check = false;
int _calibrating = 0;
bool _calibrated = false;
dll.LucamSnapshot _snap;
dll.LucamConversion _convert;
dll.LucamRgbPreviewCallback _callbackHandler;
int _callbackId;
Bitmap[] _bmps = new Bitmap[2];
int _bmpIndex = 0;
List<Mat> _chartMasks = new List<Mat>();
int _detectionCount = 0;
Point2f _lastPosition = new Point2f(0, 0);
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="preview"></param>
/// <param name="display"></param>
public Lucam(Form1 form, PictureBox preview, PictureBox display) {
_picPreview = preview;
_picDisplay = display;
_form = form;
Config.Load();
RefRGB = Config.GetFloat("ReferenceValue");
UpdateRate = Config.GetFloat("UpdateRate");
// カメラパラメータの初期値
_snap.BufferLastFrame = false;
_snap.Exposure = Config.GetFloat("Exposure");
_snap.ExposureDelay = 0;
_snap.flReserved1 = 0;
_snap.flReserved2 = 0;
_snap.Format.BinningX = 1;
_snap.Format.BinningY = 1;
_snap.Format.FlagsX = 0;
_snap.Format.FlagsY = 0;
_snap.Format.Height = 1024;
_snap.Format.PixelFormat = dll.LucamPixelFormat.PF_8;
_snap.Format.SubSampleX = 1;
_snap.Format.SubSampleY = 1;
_snap.Format.Width = 1280;
_snap.Format.X = 0;
_snap.Format.Y = 0;
_snap.Gain = Config.GetFloat("Gain");
_snap.GainBlue = Config.GetFloat("GainB");
_snap.GainGrn1 = Config.GetFloat("GainG");
_snap.GainGrn2 = _snap.GainGrn1;
_snap.GainRed = Config.GetFloat("GainR");
_snap.ShutterType = dll.LucamShutterType.GlobalShutter;
_snap.StrobeDelay = 0.1f;
_snap.StrobeFlags = 0;
_snap.Timeout = 5000;
_snap.ulReserved2 = 0;
_snap.UseHwTrigger = false;
_convert.DemosaicMethod = dll.LucamDemosaicMethod.HIGH_QUALITY;
_convert.CorrectionMatrix = dll.LucamCorrectionMatrix.LED;
}
/// <summary>
/// カメラ接続
/// </summary>
/// <returns></returns>
public bool Connect() {
if (!Config.IsLoaded()) {
ErrorMsg = "設定ファイル(Config.xml)の読み込みに失敗しました.\r\n終了します.";
return false;
}
var numCam = dll.LucamNumCameras();
if ( numCam < 1 ) {
ErrorMsg = "カメラが見つかりません.\r\n終了します.";
return false;
}
if (numCam > 1) {
ErrorMsg = "複数のカメラが見つかりました.\r\n正しいカメラを1つ接続してください.\r\n終了します.";
return false;
}
// カメラのデバイス情報取得
var lumVersion = new dll.LucamVersion[numCam];
lumVersion = api.EnumCameras();
var id = 0;
if (lumVersion[id].CameraId == 0x49f) DeviceName = "Lw110";
SerialNumber = lumVersion[id].SerialNumber.ToString();
// カメラを開く
_hCam = api.CameraOpen(1);
if (_hCam == IntPtr.Zero) {
ErrorMsg = "カメラの接続に失敗しました.\r\n他のアプリケーションでカメラを使用していないか確認してください.\r\n終了します.";
return false;
}
// プレビューコールバックの登録
_callbackHandler = new dll.LucamRgbPreviewCallback(PreviewCallback);
_callbackId = dll.LucamAddRgbPreviewCallback(_hCam, _callbackHandler, IntPtr.Zero, dll.LucamPixelFormat.PF_24);
if (_callbackId == -1) {
ErrorMsg = "コールバックの登録に失敗しました.\r\n終了します.";
return false;
}
SetCameraParam(); // カメラパラメータの設定
return true;
}
/// <summary>
/// プレビュー開始・停止
/// </summary>
/// <returns></returns>
public bool StartStopPreview() {
if (_isPreview) {
// プレビュー停止
var ret = dll.LucamStreamVideoControl(_hCam, dll.LucamStreamMode.STOP_STREAMING, _picPreview.Handle.ToInt32());
Debug.WriteLine("プレビュー停止");
if (!ret) return false;
_isPreview = false;
return true;
} else {
// プレビュー開始
var ret = dll.LucamStreamVideoControl(_hCam, dll.LucamStreamMode.START_DISPLAY, _picPreview.Handle.ToInt32());
if (!ret) return false;
_isPreview = true;
return true;
}
}
/// <summary>
/// ゲイン値の更新比率計算
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private float GetRatio(float value, float target) {
float ratio = target / value;
ratio = (ratio - 1.0f) * (value == 255.0f ? 1.0f : UpdateRate) + 1.0f;
return ratio;
}
/// <summary>
/// チャートの検出
/// </summary>
/// <param name="img"></param>
private void DetectChart(Mat img) {
// ARマーカー検出
CvAruco.DetectMarkers(img, ARDict, out var corners, out var ids,
new DetectorParameters(), out var rejectedImgPoints);
if (ids.Length < 1) return;
// マーカー座標格納
var ptsPict = new List<Point2f>();
var ptsModel = new List<Point2f>();
Point2f position = new Point2f();
for (int i = 0; i < ids.Length; i++) {
if (ids[i] == 40) {
ptsPict.AddRange(corners[i]);
ptsModel.AddRange(PointsDst40);
position = corners[i][3];
}
if (ids[i] == 41) {
ptsPict.AddRange(corners[i]);
ptsModel.AddRange(PointsDst41);
}
}
if (ptsPict.Count < 8) return;
// チャートの固定判定
var dist = (float)position.DistanceTo(_lastPosition);
if (dist < Config.GetFloat("ChartSetCriteria")) {
_detectionCount++;
} else {
_detectionCount = 0;
}
_lastPosition = position;
if (_detectionCount < Config.GetInt("ChartSetCount")) return;
// ホモグラフィの計算
var matPtsPict = Mat.FromArray(ptsPict);
var matPtsModel = Mat.FromArray(ptsModel);
var matH = Cv2.FindHomography(matPtsModel, matPtsPict);
var imgF = new Mat(1545, 810, MatType.CV_8UC3);
Cv2.WarpPerspective(img, imgF, matH, imgF.Size());
// チャートマスク作成
_chartMasks.Clear();
var roiSize = ptsPict.Count < 8 ? 60 : 80;
for (int i = 0; i < 24; i++) {
var row = i % 6;
var col = i / 6;
var x = 581 - col * 144 + (ptsPict.Count < 8 ? 10 : 0);
var y = 318 + row * 144 + (ptsPict.Count < 8 ? 10 : 0);
var roi = new Rect(x, y, roiSize, roiSize);
using (var mask = new Mat(1545, 810, MatType.CV_8U)) {
Cv2.Rectangle(mask, roi, new Scalar(255), Cv2.FILLED);
var maskF = new Mat(img.Size(), MatType.CV_8U);
Cv2.WarpPerspective(mask, maskF, matH, maskF.Size());
_chartMasks.Add(maskF);
img.SetTo(new Scalar(0, 200, 0), maskF);
}
}
Cv2.ImWrite("チャート検出結果.jpg", img);
Debug.WriteLine("チャート検出結果.jpg 保存");
_form.ShowMessage("舌診チャート検出 校正中");
_calibrating = Config.GetInt("CalibrationFrames");
}
/// <summary>
/// プレビューコールバック
/// </summary>
/// <param name="pContext"></param>
/// <param name="pData">データポインタ</param>
/// <param name="n">データサイズ</param>
/// <param name="unused"></param>
void PreviewCallback(IntPtr pContext, IntPtr pData, int n, uint unused) {
using (Mat img = Mat.FromPixelData(_snap.Format.Height, _snap.Format.Width, MatType.CV_8UC3, pData)) {
using (Mat imgt = img.T()) {
_bmps[_bmpIndex] = imgt.ToBitmap();
if (_calibrating > 0) {
var whitePatch = Cv2.Mean(imgt, _chartMasks[12]);
Debug.WriteLine($"White patch R {whitePatch.Val2:.00} G {whitePatch.Val1:.00} B {whitePatch.Val0:.00}");
if (_calibrating % Config.GetInt("CalibrationUpdateInterval") == 0) {
_snap.GainBlue *= GetRatio((float)whitePatch.Val0, Config.GetFloat("ReferenceB"));
_snap.GainGrn1 *= GetRatio((float)whitePatch.Val1, Config.GetFloat("ReferenceG"));
_snap.GainGrn2 = _snap.GainGrn1;
_snap.GainRed *= GetRatio((float)whitePatch.Val2, Config.GetFloat("ReferenceR"));
SetCameraParam();
}
_calibrating--;
if (_calibrating == 0) {
_form.ShowMessage("自動校正完了");
_form.EnableShots();
_calibrated = true;
}
}
if (!_calibrated && _calibrating == 0) {
DetectChart(imgt);
}
}
}
_form.ShowImage(_bmps[_bmpIndex]);
_bmpIndex = (_bmpIndex + 1) % 2;
if (_bmps[_bmpIndex] != null) _bmps[_bmpIndex].Dispose();
}
/// <summary>
/// 画像撮影1枚
/// </summary>
public void ShotOne() {
Shot();
}
/// <summary>
/// 複数画像撮影
/// </summary>
public void ShotMulti() {
Shot(Config.GetInt("MultiShotCount"), Config.GetInt("MultiShotInterval"));
}
/// <summary>
/// 撮影
/// </summary>
/// <param name="numImages"></param>
/// <param name="interval"></param>
private void Shot(int numImages = 1, int interval = 1000) {
SetSnapParam();
dll.LucamEnableFastFrames(_hCam, ref _snap);
var imageSize = _snap.Format.Width * _snap.Format.Height;
var rawImage = new byte[imageSize];
var rgbImage = new byte[imageSize * 3];
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);
}
}
}
rgbImage = null;
rawImage = null;
dll.LucamDisableFastFrames(_hCam);
}
/// <summary>
/// 校正 露光時間とホワイトバランスの自動調整
/// </summary>
public void Calibration() {
Debug.WriteLine("校正前");
SetSnapParam();
_calibrating = 30;
}
/// <summary>
/// 撮影パラメータの設定
/// </summary>
private void SetSnapParam() {
dll.LucamPropertyFlag flag;
dll.LucamGetProperty(_hCam, dll.LucamProperty.EXPOSURE, out _snap.Exposure, out flag);
dll.LucamGetProperty(_hCam, dll.LucamProperty.GAIN, out _snap.Gain, out flag);
dll.LucamGetProperty(_hCam, dll.LucamProperty.GAIN_BLUE, out _snap.GainBlue, out flag);
dll.LucamGetProperty(_hCam, dll.LucamProperty.GAIN_GREEN1, out _snap.GainGrn1, out flag);
dll.LucamGetProperty(_hCam, dll.LucamProperty.GAIN_GREEN2, out _snap.GainGrn2, out flag);
dll.LucamGetProperty(_hCam, dll.LucamProperty.GAIN_RED, out _snap.GainRed, out flag);
Debug.WriteLine($"SetSnapParam Exp {_snap.Exposure} Gain {_snap.Gain} GainBlue {_snap.GainBlue} GainGrn1 {_snap.GainGrn1} GainGrn2 {_snap.GainGrn2} GainRed {_snap.GainRed}");
}
/// <summary>
/// カメラパラメータの設定
/// </summary>
private void SetCameraParam() {
var flag = dll.LucamPropertyFlag.NONE;
dll.LucamSetProperty(_hCam, dll.LucamProperty.EXPOSURE, _snap.Exposure, flag);
dll.LucamSetProperty(_hCam, dll.LucamProperty.GAIN, _snap.Gain, flag);
dll.LucamSetProperty(_hCam, dll.LucamProperty.GAIN_BLUE, _snap.GainBlue, flag);
dll.LucamSetProperty(_hCam, dll.LucamProperty.GAIN_GREEN1, _snap.GainGrn1, flag);
dll.LucamSetProperty(_hCam, dll.LucamProperty.GAIN_GREEN2, _snap.GainGrn2, flag);
dll.LucamSetProperty(_hCam, dll.LucamProperty.GAIN_RED, _snap.GainRed, flag);
Debug.WriteLine($"SetCameraParam Exp {_snap.Exposure} Gain {_snap.Gain} GainBlue {_snap.GainBlue} GainGrn1 {_snap.GainGrn1} GainGrn2 {_snap.GainGrn2} GainRed {_snap.GainRed}");
}
/// <summary>
/// 動作チェック有効化
/// </summary>
public void Check() {
_check = true;
}
/// <summary>
/// カメラ切断
/// </summary>
public void Disconnect() {
if (_hCam != IntPtr.Zero) {
if (_isPreview) {
StartStopPreview();
}
api.CameraClose(_hCam);
_hCam = IntPtr.Zero;
Debug.WriteLine("カメラ切断");
}
}
}
}