﻿using System;
using System.Windows.Forms;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using TensorFlow;
using OpenCvSharp;

namespace Tongue_extraction
{
    public partial class Form1 : Form
    {
        //Use the RemoveSmallRegionDLL
        [DllImport(@"RemoveSmallRegionDLL.dll", EntryPoint = "RemoveSmallRegion", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
        extern static void RemoveSmallRegion(string name, string name2, int AreaLimit, int CheckMode, int NeihborMode);

        Mat mat_drawBox = new Mat(1024, 1280, MatType.CV_8UC3, 1); 
        Mat mat_roi = new Mat(1024, 1280, MatType.CV_8UC3, 1);
        Mat mat_input = new Mat(1024, 1280, MatType.CV_8UC3, 1);
        Mat mat_roi256 = new Mat(256, 256, MatType.CV_8UC3, 1);
        Mat mat_roisize = new Mat(1024, 1280, MatType.CV_8UC3, 1);
        Mat mat_output = new Mat(1024, 1280, MatType.CV_8UC1, 1);
        Mat mat_outputNoBox = new Mat(1024, 1280, MatType.CV_8UC1, 1);
        Mat mat_outputChanged = new Mat(1024, 1280, MatType.CV_8UC1, 1);
        Mat mat_mask = new Mat(1024, 1280, MatType.CV_8UC1, 1);
        Mat mat_extraction = new Mat(1024, 1280, MatType.CV_8UC3, 1);
        Mat mat_cropped;
        Mat mat_outputSRGNoBox = new Mat(1024, 1280, MatType.CV_8UC1, 1);
        Mat mat_outputSRG = new Mat(1024, 1280, MatType.CV_8UC1, 1);
        Mat mat_maskSRG = new Mat(1024, 1280, MatType.CV_8UC1, 1);

        public static Bitmap bitmap_bitch;

        OpenCvSharp.Rect rectangle;

        byte[] byte_inputDetection;
        byte[] byte_inputSegmentation;
        OpenCvSharp.Point P1 = new OpenCvSharp.Point();
        OpenCvSharp.Point P2 = new OpenCvSharp.Point();

        byte[] mask = new byte[200000];

        string basepath;
        string imageFile;
        string time;
        string modelFile;
        int ii = 0;
        int count;
        int areaCount;

        Rect roi = new Rect();
        int mmp = 0;
        int pmm = 0;

        int check_detection = 0;
        float max_score = 0;

        private ManualResetEvent manualReset = new ManualResetEvent(true);

        string path_infocsv = Directory.GetCurrentDirectory() + "//info//" + DateTime.Now.ToLocalTime().ToString("yyyyMMddhhmmss") + ".csv";

        string path_procList = @"ProcList.txt";

        bool IsLastFileChanged = false;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            comboBox_mode.SelectedIndex = 2;
        }

        private void Button_start_Click(object sender, EventArgs e)
        {
            button_start.Enabled = false;
            button_pause.Enabled = true;
            pictureBox_input.Image = null;
            pictureBox_input.Refresh();
            pictureBox_detection.Image = null;
            pictureBox_detection.Refresh();
            pictureBox_cropResized.Image = null;
            pictureBox_cropResized.Refresh();
            pictureBox_output.Image = null;
            pictureBox_output.Refresh();
            pictureBox_outputSRG.Image = null;
            pictureBox_outputSRG.Refresh();
            pictureBox_maskSRG.Image = null;
            pictureBox_maskSRG.Refresh();
            pictureBox_extraction.Image = null;
            pictureBox_extraction.Refresh();
            pictureBox_last.Image = null;
            pictureBox_last.Refresh();
            comboBox_mode.Enabled = false;

            if (comboBox_mode.SelectedIndex == 0)
            {
                // 画像のみ入力し，マスク等を得る
                this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.BackgroundWorker1_DoWork_Image);
                backgroundWorker1.RunWorkerAsync();
            }
            else if (comboBox_mode.SelectedIndex == 1)
            {
                // 画像と校正画像を入力し，Lab色を得る
                this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.BackgroundWorker1_DoWork_ImageandCalib);
                backgroundWorker1.RunWorkerAsync();
            }
            else if (comboBox_mode.SelectedIndex == 2)
            {
                // TIASフォルダを入力し，Lab色を得る
                this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.BackgroundWorker1_DoWork_TIASdata);
                backgroundWorker1.RunWorkerAsync();
            }
            else
            {
                MessageBox.Show("Please select the mode on the combobox");
            }
        }

        public static class ImageUtil
        {
            // Convert the image in filename to a Tensor suitable as input to the Inception model.
            public static TFTensor CreateTensorFromImageFile(byte[] contents, TFDataType destinationDataType = TFDataType.UInt8)
            {
                // DecodeJpeg uses a scalar String-valued tensor as input.
                var tensor = TFTensor.CreateString(contents);

                TFGraph graph;
                TFOutput input, output;

                // Construct a graph to normalize the image
                ConstructGraphToNormalizeImage(out graph, out input, out output, destinationDataType);

                // Execute that graph to normalize this one image
                using (var session = new TFSession(graph))
                {
                    var normalized = session.Run(
                             inputs: new[] { input },
                             inputValues: new[] { tensor },
                             outputs: new[] { output });

                    if (session != null)
                    {
                        session.Dispose();
                    }
                    if (tensor != null)
                    {
                        tensor.Dispose();
                    }
                    if (graph != null)
                    {
                        graph.Dispose();
                    }
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    GC.Collect();

                    return normalized[0];
                }
            }

            // The inception model takes as input the image described by a Tensor in a very
            // specific normalized format (a particular image size, shape of the input tensor,
            // normalized pixel values etc.).
            //
            // This function constructs a graph of TensorFlow operations which takes as
            // input a JPEG-encoded string and returns a tensor suitable as input to the
            // inception model.

            public static void ConstructGraphToNormalizeImage(out TFGraph graph, out TFOutput input, out TFOutput output, TFDataType destinationDataType = TFDataType.UInt8)
            {
                const int W = 256;
                const int H = 256;
                const float Mean = 0;
                const float Scale = 1;
                graph = new TFGraph();
                input = graph.Placeholder(TFDataType.String);
                output = graph.Cast(graph.Div(
                    x: graph.Sub(
                        x: graph.ResizeBilinear(
                            images: graph.ExpandDims(
                                input: graph.Cast(
                                    graph.DecodeJpeg(contents: input, channels: 3), DstT: destinationDataType),
                                dim: graph.Const(0, "make_batch")),
                            size: graph.Const(new int[] { W, H }, "size")),
                        y: graph.Const(Mean, "mean")),
                    y: graph.Const(Scale, "scale")), destinationDataType);
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
            }
        }

        public static class ImageUtil2
        {
            // Convert the image in filename to a Tensor suitable as input to the Inception model.
            public static TFTensor CreateTensorFromImageFile(byte[] contents, TFDataType destinationDataType = TFDataType.Float)
            {
                // DecodeJpeg uses a scalar String-valued tensor as input.
                var tensor = TFTensor.CreateString(contents);

                TFGraph graph;
                TFOutput input, output;

                // Construct a graph to normalize the image
                ConstructGraphToNormalizeImage(out graph, out input, out output, destinationDataType);

                // Execute that graph to normalize this one image
                using (var session = new TFSession(graph))
                {
                    var normalized = session.Run(
                             inputs: new[] { input },
                             inputValues: new[] { tensor },
                             outputs: new[] { output });

                    if (session != null)
                    {
                        session.Dispose();
                    }
                    if (tensor != null)
                    {
                        tensor.Dispose();
                    }
                    if (graph != null)
                    {
                        graph.Dispose();
                    }
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    GC.Collect();

                    return normalized[0];
                }
            }

            // The inception model takes as input the image described by a Tensor in a very
            // specific normalized format (a particular image size, shape of the input tensor,
            // normalized pixel values etc.).
            //
            // This function constructs a graph of TensorFlow operations which takes as
            // input a JPEG-encoded string and returns a tensor suitable as input to the
            // inception model.

            public static void ConstructGraphToNormalizeImage(out TFGraph graph, out TFOutput input, out TFOutput output, TFDataType destinationDataType = TFDataType.Float)
            {
                const int W = 256;
                const int H = 256;
                //const int W = 512;
                //const int H = 512;

                const float Mean = 128;
                const float Scale = 128;
                graph = new TFGraph();
                input = graph.Placeholder(TFDataType.String);
                output = graph.Cast(graph.Div(
                    x: graph.Sub(
                        x: graph.ResizeBilinear(
                            images: graph.ExpandDims(
                                input: graph.Cast(
                                    graph.DecodeJpeg(contents: input, channels: 3), DstT: TFDataType.Float),
                                dim: graph.Const(0, "make_batch")),
                            size: graph.Const(new int[] { W, H }, "size")),
                        y: graph.Const(Mean, "mean")),
                    y: graph.Const(Scale, "scale")), destinationDataType);
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
            }
        }

        public static byte[] Bitmap2Byte(Bitmap bitmap)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                bitmap.Save(stream, ImageFormat.Jpeg);
                byte[] data = new byte[stream.Length];
                stream.Seek(0, SeekOrigin.Begin);
                stream.Read(data, 0, Convert.ToInt32(stream.Length));
                return data;
            }
        }

        private static string DownloadDefaultModel(string dir)
        {
            var modelFile = Path.Combine(dir, "pruning101_step11999.pb");
            return modelFile;
        }

        private static string DownloadDefaultModel_noBoxPix2Pix(string dir)
        {
            var modelFile = Path.Combine(dir, "424_256_64_5999_scale300_enhancment_L1loss0.02001.pb");
            return modelFile;
        }

        public static Bitmap ToGrayBitmap(byte[] rawValues, int width, int height)
        {
            //// Apply for a target bitmap variable and lock its memory area
            Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
            BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height),
             ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);

            //// Get image parameters
            int stride = bmpData.Stride;  // Width of the scan line
            int offset = stride - width;  // Show gap between width and scan line width
            IntPtr iptr = bmpData.Scan0;  // Get the memory start position of bmpData
            int scanBytes = stride * height;// Use stride width to indicate that this is the size of the memory area

            //// The following is to convert the original display size byte array to the byte array actually stored in memory.
            int posScan = 0, posReal = 0;// Set two position pointers respectively, pointing to the source array and the target array
            byte[] pixelValues = new byte[scanBytes];  //Allocate memory for the target array

            for (int x = 0; x < height; x++)
            {
                //// The following loop section is a simulated line scan
                for (int y = 0; y < width; y++)
                {
                    pixelValues[posScan++] = rawValues[posReal++];
                }
                posScan += offset;  //At the end of the line scan, move the target position pointer over that "gap"
            }

            //// Use Marshal's Copy method to copy the just obtained memory byte array into BitmapData.
            System.Runtime.InteropServices.Marshal.Copy(pixelValues, 0, iptr, scanBytes);
            bmp.UnlockBits(bmpData);  // Unlock the memory area

            //// The following code is to modify the index table of the generated bitmap, from pseudo color to grayscale
            ColorPalette tempPalette;
            using (Bitmap tempBmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed))
            {
                tempPalette = tempBmp.Palette;
            }
            for (int i = 0; i < 256; i++)
            {
                tempPalette.Entries[i] = Color.FromArgb(i, i, i);
            }

            bmp.Palette = tempPalette;

            //// The algorithm ends here and returns the result.
            return bmp;
        }

        private void BackgroundWorker1_DoWork_Image(object sender, DoWorkEventArgs e)
        {
            MessageBox.Show("this mode isn't implemented");
        }

        private void BackgroundWorker1_DoWork_ImageandCalib(object sender, DoWorkEventArgs e)
        {

            // boundingboxなどのinfo出力用
            var sw  = new StreamWriter(path_infocsv, false, System.Text.Encoding.GetEncoding("shift_jis"));
            //　出力用csvの準備
            sw.Write(
                "image" + "," +
                "L1" + "," + "a1" + "," + "b1" + "," +
                "L2" + "," + "a2" + "," + "b2" + "," +
                "L3" + "," + "a3" + "," + "b3" + "," +
                "L4" + "," + "a4" + "," + "b4"
                );
            
            sw.WriteLine();
            sw.Close();

            using (MemoryStream ms = new MemoryStream())
            {
                // ディレクトリglobの取得
                var glob_dir = Directory.GetDirectories("input");

                for (int a = 0; a < glob_dir.Length; a++)
                {
                    manualReset.WaitOne();

                    // 画像と校正ファイルのパス
                    var glob_file = Directory.GetFiles(glob_dir[a]);
                    var path_calib = glob_file.Where(n => n.Contains("csv")).ToList()[0];
                    var path_image = glob_file.Where(n => n.Contains("bmp") || n.Contains("png")).ToList()[0];
                    var path_base = Directory.GetCurrentDirectory();
                    var name_image = Path.GetFileName(path_image);
                    var name_dir = Path.GetFileName(glob_dir[a]);

                    // ステータスの表示
                    Invoke((MethodInvoker)delegate
                    {
                        label_processingFileName.Text = "Processing File: " + "\\" + name_dir;
                        count = a + 1;
                        label_totalProgress.Text = "Total Progress: " + count + "/" + glob_dir.Length;
                    });

                    // 入力画像読み込み・表示
                    mat_input = Cv2.ImRead(path_image, ImreadModes.Color);

                    bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_input);
                    Invoke((MethodInvoker)delegate
                    {
                        pictureBox_input.Image = bitmap_bitch;
                        pictureBox_input.Refresh();
                    });

                    // Detection
                    label8.BackColor = Color.White;
                    label1.BackColor = Color.Red;
                    mat_drawBox = mat_input.Clone();
                    mat_cropped = new Mat(mat_input.Size(), MatType.CV_8UC3, 1);
                    byte_inputDetection = Bitmap2Byte(bitmap_bitch);
                    using (var graph = new TFGraph())
                    {
                        var model = File.ReadAllBytes(path_base + "/Detection_Normal.pb");
                        graph.Import(model, "");

                        using (var session = new TFSession(graph))
                        {
                            var tensor = ImageUtil.CreateTensorFromImageFile(byte_inputDetection, TFDataType.UInt8);

                            var runner = session.GetRunner();
                            runner
                                .AddInput(graph["image_tensor"][0], tensor)
                                .Fetch("detection_boxes", "detection_scores", "detection_classes", "num_detections");

                            var output = runner.Run();
                            var boxes = (float[,,])output[0].GetValue();
                            var scores = (float[,])output[1].GetValue();
                            var classes = (float[,])output[2].GetValue();
                            var detections = (float[])output[3].GetValue();
                            check_detection = 0;
                            max_score = 0;
                            for (int i = 0; i < scores.Length; i++)
                            {
                                if ((scores[0, i] > 0.5) && (scores[0, i] > max_score))
                                {
                                    max_score = scores[0, i];
                                    float y_min = boxes[0, i, 0] * (float)bitmap_bitch.Height;
                                    float x_min = boxes[0, i, 1] * (float)bitmap_bitch.Width;
                                    float y_max = boxes[0, i, 2] * (float)bitmap_bitch.Height;
                                    float x_max = boxes[0, i, 3] * (float)bitmap_bitch.Width;
                                    P1.X = (int)x_min;
                                    P1.Y = (int)y_min;
                                    P2.X = (int)x_max;
                                    P2.Y = (int)y_max;
                                    Cv2.Rectangle(mat_drawBox, P1, P2, new Scalar(0, 255, 0), 5);
                                    rectangle.X = (int)x_min;
                                    rectangle.Y = (int)y_min;
                                    rectangle.Width = (int)(x_max - x_min);
                                    rectangle.Height = (int)(y_max - y_min);

                                    check_detection = 1;
                                }
                            }
                        }
                    }
                    // Detection結果表示・保存
                    mat_drawBox.SaveImage(path_base + "\\detection" + "\\" + name_dir + ".bmp");
                    bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_drawBox);
                    Invoke((MethodInvoker)delegate
                    {
                        pictureBox_detection.Image = bitmap_bitch;
                        pictureBox_detection.Refresh();
                    });
                    label1.BackColor = Color.White;

                    // 舌検出領域でcrop
                    label2.BackColor = Color.Red;
                    for (int i = P1.Y; i < P2.Y; i++)
                    {
                        for (int j = P1.X; j < P2.X; j++)
                        {
                            Vec3b pix = mat_input.At<Vec3b>(i, j);
                            mat_cropped.Set<Vec3b>(i, j, pix);
                        }
                    }
                    mat_cropped.SaveImage(path_base + "\\cropped" + "\\" + name_dir + ".bmp");

                    // 舌検出領域でresize
                    OpenCvSharp.Size size_roi = new OpenCvSharp.Size();
                    size_roi.Height = rectangle.Height;
                    size_roi.Width = rectangle.Width;
                    roi = new Rect(P1, size_roi);


                    // 20210201
                    var mat_input2 = mat_input.Clone();
                    mat_input2 = mat_input2 * 1.5;

                    mat_roisize = mat_input2.Clone(roi);
                    Cv2.Resize(mat_roisize, mat_roi, mat_roi256.Size());
                    mat_roi.SaveImage(path_base + "\\cropresized" + "\\" + name_dir + ".bmp");

                    bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_roi);
                    Invoke((MethodInvoker)delegate
                    {
                        pictureBox_cropResized.Image = bitmap_bitch;
                        pictureBox_cropResized.Refresh();
                    });
                    label2.BackColor = Color.White;

                    // Segmenation
                    label3.BackColor = Color.Red;
                    
                    byte_inputSegmentation = Bitmap2Byte(bitmap_bitch);
                    Thread.Sleep(1000);
                    modelFile = DownloadDefaultModel(path_base);
                    ii = 0;
                    using (var graph = new TFGraph())
                    {
                        var model = File.ReadAllBytes(modelFile);
                        graph.Import(model, "");

                        using (var session = new TFSession(graph))
                        {
                            var tensor = ImageUtil2.CreateTensorFromImageFile(byte_inputSegmentation);
                            var runner = session.GetRunner();
                            runner
                            //.AddInput(graph["generator/input_image"][0], tensor)
                            //.Fetch(graph["generator/prediction"][0]);

                            .AddInput(graph["input_image"][0], tensor)
                            .Fetch(graph["generator1/decoder_1/Tanh"][0]);

                            var output = runner.Run();
                            float[,,,] resultfloat = (float[,,,])output[0].GetValue(jagged: false);

                            for (int p = 0; p < 256; p++)
                            {
                                for (int q = 0; q < 256; q++)
                                {
                                    float check = resultfloat[0, p, q, 0];
                                    if (check < 0)
                                    {
                                        mask[ii] = 0;
                                    }
                                    else
                                    {
                                        mask[ii] = 255;
                                    }
                                    ii++;
                                }
                            }
                        }
                    }
                    GC.Collect();
                    Thread.Sleep(1000);
                    
                    // segmentation結果表示・保存
                    bitmap_bitch = ToGrayBitmap(mask, 256, 256);
                    mat_output = OpenCvSharp.Extensions.BitmapConverter.ToMat(bitmap_bitch);
                    bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_output);
                    Invoke((MethodInvoker)delegate
                    {
                        pictureBox_output.Image = bitmap_bitch;
                        pictureBox_output.Refresh();
                    });
                    label3.BackColor = Color.White;
                    label4.BackColor = Color.Red;
                    mat_output.SaveImage(path_base + "\\output256" + "\\" + name_dir + ".bmp");

                    // 後処理（領域拡張法）でノイズ除去　iteration 2
                    try
                    {
                        RemoveSmallRegion(path_base + "\\output256" + "\\" + name_dir + ".bmp", path_base + "\\output_changed1" + "\\" + name_dir + ".bmp", 500, 1, 1);
                        RemoveSmallRegion(path_base + "\\output_changed1" + "\\" + name_dir + ".bmp", path_base + "\\output_changed2" + "\\" + name_dir + ".bmp", 500, 0, 0);
                    }
                    catch
                    {
                        MessageBox.Show("Error: Unable to reprocess! Please check is there [RemoveSmallRegionDLL.dll] file in floder?", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    }
                    mat_outputSRG = new Mat(path_base + "\\output_changed2" + "\\" + name_dir + ".bmp", ImreadModes.GrayScale);
                    bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_outputSRG);
                    Invoke((MethodInvoker)delegate
                    {
                        pictureBox_outputSRG.Image = bitmap_bitch;
                        pictureBox_outputSRG.Refresh();
                    });
                    label4.BackColor = Color.White;
                    label5.BackColor = Color.Red;

                    // バウンディングボックスのサイズに戻す
                    Cv2.Resize(mat_outputSRG, mat_outputChanged, mat_roisize.Size());
                    mat_outputChanged.SaveImage(path_base + "\\output_resized" + "\\" + name_dir + ".bmp"); ;

                    // 入力と同じサイズでマスクを作成(なんかたまに不安定．resizeで微妙にサイズ変わってrange overかも)
                    // 気休め
                    Thread.Sleep(100);
                    GC.Collect();

                    mat_mask = new Mat(mat_input.Size(), MatType.CV_8UC1, 0);
                    var y_mask = 0;
                    for (int y = P1.Y; y < P2.Y; y++)
                    {
                        if (y_mask >= mat_outputChanged.Height)
                            break;

                        var x_mask = 0;
                        for (int x = P1.X; x < P2.X; x++)
                        {
                            if (x_mask >= mat_outputChanged.Width)
                                break;

                            int pix = mat_outputChanged.At<int>(y_mask, x_mask);
                            mat_mask.Set<int>(y, x, pix);
                            x_mask++;
                        }
                        y_mask++;
                    }
                    mmp = 0;
                    Cv2.Resize(mat_mask, mat_mask, mat_input.Size());
                    mat_mask.SaveImage(path_base + "\\mask" + "\\" + name_dir + ".bmp");

                    // ノイズ処理
                    try
                    {
                        RemoveSmallRegion(path_base + "\\mask" + "\\" + name_dir + ".bmp", path_base + "\\mask_changed1" + "\\" + name_dir + ".bmp", 500, 1, 1);
                        RemoveSmallRegion(path_base + "\\mask_changed1" + "\\" + name_dir + ".bmp", path_base + "\\mask_changed2" + "\\" + name_dir + ".bmp", 500, 0, 0);
                    }
                    catch
                    {
                        MessageBox.Show("Error: Unable to reprocess! Please check is there [RemoveSmallRegionDLL.dll] file in floder?", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    }
                    mat_maskSRG = new Mat(path_base + "\\mask_changed2" + "\\" + name_dir + ".bmp", ImreadModes.GrayScale);
                    Cv2.Threshold(mat_maskSRG, mat_maskSRG, 128, 255, ThresholdTypes.Binary);

                    // 2値マスクの最終結果
                    mat_maskSRG.SaveImage(path_base + "\\mask_final" + "\\" + name_dir + ".bmp");
                    bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_maskSRG);
                    Invoke((MethodInvoker)delegate
                    {
                        pictureBox_maskSRG.Image = bitmap_bitch;
                        pictureBox_maskSRG.Refresh();
                    });
                    label5.BackColor = Color.White;

                    // 元画像とマスクを合わせて，舌領域を抜き出す　
                    // todo: opencv等によるマスク処理と領域計算へ
                    label6.BackColor = Color.Red;
                    mat_extraction = mat_input.Clone();
                    areaCount = 0;
                    for (int i = 0; i < mat_input.Height; i++)
                    {
                        for (int j = 0; j < mat_input.Width; j++)
                        {
                            Vec3b pix = mat_extraction.At<Vec3b>(i, j);
                            if (mat_maskSRG.At<int>(i, j) == 0)
                            {
                                pix[0] = (byte)(255);
                                pix[1] = (byte)(255);
                                pix[2] = (byte)(255);
                                mat_extraction.Set<Vec3b>(i, j, pix);
                            }
                            else
                            {
                                pix[0] = (byte)(mat_extraction.At<Vec3b>(i, j).Item0);
                                pix[1] = (byte)(mat_extraction.At<Vec3b>(i, j).Item1);
                                pix[2] = (byte)(mat_extraction.At<Vec3b>(i, j).Item2);
                                mat_extraction.Set<Vec3b>(i, j, pix);
                                areaCount++;
                            }
                        }
                    }
                    mat_extraction.SaveImage(path_base + "\\extraction" + "\\" + name_dir + ".bmp");
                    bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_extraction);
                    Invoke((MethodInvoker)delegate
                    {
                        pictureBox_extraction.Image = bitmap_bitch;
                        pictureBox_extraction.Refresh();
                    });
                    label6.BackColor = Color.White;

                    /// 5点クリックによる色抽出処理
                    var path_colorMatrixXYZ = "xyz.txt";

                    // 色抽出処理のクラス
                    ColorExtractor ce = new ColorExtractor();

                    // マスクされた舌領域画像の作成
                    Mat mat_finalMask = mat_maskSRG.Clone();
                    Cv2.ImWrite(path_base + "\\final_mask" + "\\" + name_dir + ".bmp", mat_finalMask);

                    Mat mat_maskedImg = new Mat();
                    mat_input.CopyTo(mat_maskedImg, mat_finalMask);

                    // 5点クリック法（2010石川）
                    List<OpenCvSharp.Point> list_5points_3 = ce.Get5points(mat_finalMask, ColorExtractor.FivePointMethod.Method3);

                    // 8領域の取得
                    List<OpenCvSharp.Point> list_8area_3 = ce.Get8area(list_5points_3);

                    // 領域の表示
                    var mat_areaDicision = ce.ShowResult(mat_input.Clone(), list_5points_3, list_8area_3);

                    // 色抽出
                    List<OpenCvSharp.Scalar> list_4Bgr = ce.Get4colors(mat_maskedImg, list_8area_3);

                    // 色変換(RGB->XYZ->Lab)
                    List<OpenCvSharp.Scalar> list_4Lab = ce.Calc4Lab(list_4Bgr, path_calib, path_colorMatrixXYZ);

                    // 保存
                    string CSVfilename = path_base + "\\color" + "\\" + name_dir + ".csv";
                    FileStream CSV_file = File.Open(CSVfilename, FileMode.OpenOrCreate, FileAccess.Write);
                    CSV_file.Seek(0, SeekOrigin.Begin);
                    CSV_file.SetLength(0);
                    CSV_file.Close();

                    StreamWriter CSV_data = new StreamWriter(CSVfilename);
                    CSV_data.WriteLine("Area,R,G,B,L,a,b");
                    for (int i = 0; i < list_4Bgr.Count(); i++)
                    {
                        string str = (i + 1).ToString() + ",";
                        str +=
                            list_4Bgr[i].Val2.ToString("0.0000") + "," +
                            list_4Bgr[i].Val1.ToString("0.0000") + "," +
                            list_4Bgr[i].Val0.ToString("0.0000") + "," +
                            list_4Lab[i].Val0.ToString("0.0000") + "," +
                            list_4Lab[i].Val1.ToString("0.0000") + "," +
                            list_4Lab[i].Val2.ToString("0.0000");
                        CSV_data.WriteLine(str);
                    }
                    CSV_data.Close();

                    // 廃棄
                    GC.Collect();
                    System.Threading.Thread.Sleep(100);

                    mat_areaDicision.SaveImage(path_base + "\\autoAreaDecision" + "\\" + name_dir + ".bmp");
                    bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_areaDicision);
                    Invoke((MethodInvoker)delegate
                    {
                        pictureBox_last.Image = bitmap_bitch;
                        pictureBox_last.Refresh();
                    });
                    label7.BackColor = Color.White;

                    // 処理log
                    label8.BackColor = Color.Red;
                    time = DateTime.Now.ToLocalTime().ToString();
                    File.AppendAllText("Log.txt ", time + " " + "\\" + name_dir + ".bmp" + " Done!\n");

                    // info出力
                    sw = new StreamWriter(path_infocsv, true, System.Text.Encoding.GetEncoding("shift_jis"));

                    string stra = "";
                    for (int i = 0; i < list_4Bgr.Count(); i++)
                    {
                        stra +=
                            list_4Lab[i].Val0.ToString("0.0000") + "," +
                            list_4Lab[i].Val1.ToString("0.0000") + "," +
                            list_4Lab[i].Val2.ToString("0.0000") + ",";
                    }
                    sw.Write(
                            name_dir.ToString() + "," +
                            stra
                            );
                    sw.Write("\n");

                    sw.Close();
                    // 破棄

                }
                GC.Collect();
                MessageBox.Show("Finished!");

                Invoke((MethodInvoker)delegate
                {
                    button_start.Enabled = true;
                    button_pause.Enabled = false;
                    label_processingFileName.Text = "Processing File: None";
                });
            }
        }

        // Debugs
        private void BackgroundWorker1_DoWork_TIASdata(object sender, DoWorkEventArgs e)
        {
            // boundingboxなどのinfo出力用
            var sw = new StreamWriter(path_infocsv, false, System.Text.Encoding.GetEncoding("shift_jis"));
            //　出力用csvの準備
            sw.WriteLine(
                "image" + "," +
                "top left X" + "," + "top left Y" + "," + "bottom right X" + "," + "bottom right Y" + "," +
                "Width" + "," + "Height" + "," + "Area");
            sw.Close();

            // TIASフォルダのパスを取得
            var path_procBase = "";
            Invoke((MethodInvoker)delegate
            {
                //ダイアログを表示する
                if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
                {
                    path_procBase = folderBrowserDialog1.SelectedPath;
                    label_processingFileName.Text = "処理フォルダ: " + path_procBase;
                    Console.WriteLine("処理フォルダ: " + path_procBase);
                }
                else
                {
                    MessageBox.Show("プログラムを再起動してください。");
                    return;
                }
            });
            

            // 色抽出（TongueAnalysisbin2.3）のため，Proclistを初期化
            FileStream fs = File.Open(path_procList, FileMode.OpenOrCreate, FileAccess.Write);
            fs.Seek(0, SeekOrigin.Begin);
            fs.SetLength(0);
            StreamWriter sw_ = new StreamWriter(fs);
            sw_.Write("BASEDIR=" + Directory.GetCurrentDirectory() + "\\input");
            sw_.WriteLine();
            sw_.Close();
            fs.Close();

            try
            {
                // inputとoutputフォルダを空にする ToDo: 過去に処理したデータがあれば再利用
                var _target = new DirectoryInfo("input");
                foreach (FileInfo file in _target.GetFiles())
                    file.Delete();
                foreach (DirectoryInfo dir in _target.GetDirectories())
                    dir.Delete(true);
                _target = new DirectoryInfo("output");
                foreach (FileInfo file in _target.GetFiles())
                    file.Delete();
                foreach (DirectoryInfo dir in _target.GetDirectories())
                    dir.Delete(true);

                // 選択されたTIASdirの全日付dir
                var glob_tias_dirs = Directory.GetDirectories(path_procBase);
                for (int a = 0; a < glob_tias_dirs.Length; a++)
                {
                    // 全日付dirに入っている全dir
                    var glob_date_dirs = Directory.GetDirectories(glob_tias_dirs[a]);

                    // 日付dirをコピー
                    var path_data_dst = @"input\" + Path.GetFileName(glob_tias_dirs[a]);
                    if (!Directory.Exists(path_data_dst))
                    {
                        Directory.CreateDirectory(path_data_dst);
                    }
                    else
                    {
                        Console.WriteLine(path_data_dst + "は既に存在します");
                    }

                    // 画像dirと校正dir
                    var path_calib_dirs = glob_date_dirs.Where(n => n.Contains("校正")).ToList();
                    var path_notCalib_dirs = glob_date_dirs.Where(n => !n.Contains("校正")).ToList();
                    var path_img_dirs = path_notCalib_dirs.Where(n => File.Exists(n + @"\Shot0001.bmp")).ToList();

                    // yyyy-MM-dd
                    var st_time_tiasDir = Path.GetFileName(glob_tias_dirs[a]).ToString();

                    // 色抽出（TongueAnalysisbin2.3）のため，Proclistを記述
                    sw_ = new StreamWriter(path_procList, true);
                    sw_.WriteLine(st_time_tiasDir);
                    sw_.Close();

                    // 画像dirに対して処理
                    for (int b = 0; b < path_img_dirs.Count(); b++)
                    {
                        Console.WriteLine("img dir:");
                        Console.WriteLine(path_img_dirs[b]);

                        // もし舌裏撮影なら飛ばす
                        if (File.Exists(path_img_dirs[b] + @"\NShot0001.bmp"))
                            continue;
                        // もし舌画像がなければ飛ばす
                        if (!File.Exists(path_img_dirs[b] + @"\Shot0001.bmp"))
                            continue;

                        // HH_mm_ss-患者ID-来院回数
                        var st_time_imgDir = Path.GetFileName(path_img_dirs[b]).ToString().Substring(0, 8);
                        Console.WriteLine(st_time_imgDir);

                        // 日付時刻文字列をDatetimeフォーマットへ変換
                        string strTime = st_time_tiasDir + "-" + st_time_imgDir;
                        string format = "yyyy-MM-dd-HH_mm_ss";
                        DateTime time_img_dir = DateTime.ParseExact(strTime, format, null);

                        // 舌画像dirから見て最新calib_dirを探索
                        var path_newCalibDir = path_calib_dirs[0];
                        foreach (var path_calibDir in path_calib_dirs)
                        {
                            string target = st_time_tiasDir + "-" + Path.GetFileName(path_calibDir).ToString().Substring(0, 8);
                            DateTime time_target = DateTime.ParseExact(target, format, null);

                            string newone = st_time_tiasDir + "-" + Path.GetFileName(path_newCalibDir).ToString().Substring(0, 8);
                            DateTime time_newone = DateTime.ParseExact(newone, format, null);

                            // imgDirより前に作成され，かつその中で最新
                            if (time_img_dir > time_target)
                                if (time_newone < time_target)
                                    path_newCalibDir = path_calibDir;
                        }
                        Console.WriteLine("入力画像dir");
                        Console.WriteLine(path_img_dirs[b]);
                        Console.WriteLine("入力校正dir");
                        Console.WriteLine(path_newCalibDir);

                        // それぞれのファイルとフォルダーをコピー
                        // (条件　処理が二回目以降でinfo.csvが既に存在するなら，info.csvを生成しない)
                        const string DEFAULT_FILENAME = "Shot0001.bmp";
                        var path_imgdir_dst = path_data_dst + "\\" + Path.GetFileName(path_img_dirs[b]);
                        if (!Directory.Exists(path_imgdir_dst))
                        {
                            Directory.CreateDirectory(path_imgdir_dst);
                            File.Copy(path_img_dirs[b] + "\\" + DEFAULT_FILENAME, path_imgdir_dst + "\\" + DEFAULT_FILENAME);
                        }
                        else
                        {
                            Console.WriteLine(path_imgdir_dst + "は存在します");
                        }
                        var path_calibdir_dst = path_data_dst + "\\" + Path.GetFileName(path_newCalibDir);
                        if (!Directory.Exists(path_calibdir_dst))
                        {
                            Directory.CreateDirectory(path_calibdir_dst);
                            File.Copy(path_newCalibDir + "\\" + DEFAULT_FILENAME, path_calibdir_dst + "\\" + DEFAULT_FILENAME);
                        }
                        else
                        {
                            Console.WriteLine(path_imgdir_dst + "は存在します");
                        }
                    }
                }
                GC.Collect();
            }
            catch
            {
                Console.WriteLine("フォルダの読み込み，校正ファイルの検索における例外");
                MessageBox.Show("選択されたフォルダはTIASデータフォルダではありません\nプログラムを再起動してください。");
                return;
            }
            

            using (MemoryStream ms = new MemoryStream())
            {
                // ディレクトリglobの取得
                var glob_tias_dirs_ = Directory.GetDirectories("input");
                for (int a = 0; a < glob_tias_dirs_.Length; a++)
                {
                    // tiasDirに入っている全dir
                    var glob_date_dirs = Directory.GetDirectories(glob_tias_dirs_[a]);

                    // 画像dirと校正dir
                    var path_calib_dirs = glob_date_dirs.Where(n => n.Contains("校正")).ToList();
                    var path_img_dirs = glob_date_dirs.Where(n => !n.Contains("校正")).ToList();

                    // yyyy-MM-dd
                    var st_time_tiasDir = Path.GetFileName(glob_tias_dirs_[a]).ToString();
                    Console.WriteLine(st_time_tiasDir.ToString());
                    
                    // 画像dir
                    for (int b = 0; b < path_img_dirs.Count(); b++)
                    {
                        Console.WriteLine(path_img_dirs.Count());
                        Console.WriteLine(path_img_dirs[b]);

                        // HH_mm_ss-患者ID-来院回数
                        var st_time_imgDir = Path.GetFileName(path_img_dirs[b]).ToString().Substring(0, 8);
                        Console.WriteLine(st_time_imgDir);

                        // 日付時刻文字列をDatetimeフォーマットへ変換
                        string strTime = st_time_tiasDir + "-" + st_time_imgDir;
                        string format = "yyyy-MM-dd-HH_mm_ss";
                        DateTime time_img_dir = DateTime.ParseExact(strTime, format, null);

                        // 舌画像dirから見て最新calib_dirを探索
                        var path_newCalibDir = path_calib_dirs[0];
                        foreach (var path_calibDir in path_calib_dirs)
                        {
                            string target = st_time_tiasDir + "-" + Path.GetFileName(path_calibDir).ToString().Substring(0, 8);
                            DateTime time_target = DateTime.ParseExact(target, format, null);

                            string newone = st_time_tiasDir + "-" + Path.GetFileName(path_newCalibDir).ToString().Substring(0, 8);
                            DateTime time_newone = DateTime.ParseExact(newone, format, null);

                            // imgDirより前に作成され，かつその中で最新
                            if (time_img_dir > time_target)
                                if (time_newone < time_target)
                                    path_newCalibDir = path_calibDir;
                        }
                        Console.WriteLine("入力画像dir");
                        Console.WriteLine(path_img_dirs[b]);
                        Console.WriteLine("入力校正dir");
                        Console.WriteLine(path_newCalibDir);

                        // 画像に対する繰り返し(今は最も古い画像(0001.bmp)のみを処理，ゆくゆくは全画像(0001-0200)を処理)
                        var glob_image_path = Directory.GetFiles(path_img_dirs[b]).Where(n => n.Contains(".bmp")); ;
                        foreach (var path_img in glob_image_path)
                        {
                            // DeepTIAS処理
                            manualReset.WaitOne();

                            // 画像と校正ファイルのパス
                            var path_calib = Directory.GetFiles(path_newCalibDir).Where(n => n.Contains("Shot0001.bmp")).ToList()[0];
                            var path_base = Directory.GetCurrentDirectory();
                            var name_image = Path.GetFileName(path_img);
                            var name_dir = Path.GetFileName(st_time_tiasDir);

                            // ステータスの表示
                            Invoke((MethodInvoker)delegate
                            {
                                label_processingFileName.Text = "Processing File: " + name_dir + "\\" + Path.GetFileName(path_img_dirs[b]);
                                count = a + 1;
                                label_totalProgress.Text = "Total Progress: " + count + "/" + glob_tias_dirs_.Length;
                            });

                            // 入力画像読み込み・表示
                            mat_input = Cv2.ImRead(path_img, ImreadModes.Color);

                            bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_input);
                            Invoke((MethodInvoker)delegate
                            {
                                pictureBox_input.Image = bitmap_bitch;
                                pictureBox_input.Refresh();
                            });

                            // Detection
                            label8.BackColor = Color.White;
                            label1.BackColor = Color.Red;
                            mat_drawBox = mat_input.Clone();
                            mat_cropped = new Mat(mat_input.Size(), MatType.CV_8UC3, 1);
                            byte_inputDetection = Bitmap2Byte(bitmap_bitch);
                            using (var graph = new TFGraph())
                            {
                                var model = File.ReadAllBytes(path_base + "/Detection_Normal.pb");
                                graph.Import(model, "");

                                using (var session = new TFSession(graph))
                                {
                                    var tensor = ImageUtil.CreateTensorFromImageFile(byte_inputDetection, TFDataType.UInt8);

                                    var runner = session.GetRunner();
                                    runner
                                        .AddInput(graph["image_tensor"][0], tensor)
                                        .Fetch("detection_boxes", "detection_scores", "detection_classes", "num_detections");

                                    var output = runner.Run();
                                    var boxes = (float[,,])output[0].GetValue();
                                    var scores = (float[,])output[1].GetValue();
                                    var classes = (float[,])output[2].GetValue();
                                    var detections = (float[])output[3].GetValue();
                                    check_detection = 0;
                                    max_score = 0;
                                    for (int i = 0; i < scores.Length; i++)
                                    {
                                        if ((scores[0, i] > 0.5) && (scores[0, i] > max_score))
                                        {
                                            max_score = scores[0, i];
                                            float y_min = boxes[0, i, 0] * (float)bitmap_bitch.Height;
                                            float x_min = boxes[0, i, 1] * (float)bitmap_bitch.Width;
                                            float y_max = boxes[0, i, 2] * (float)bitmap_bitch.Height;
                                            float x_max = boxes[0, i, 3] * (float)bitmap_bitch.Width;
                                            P1.X = (int)x_min;
                                            P1.Y = (int)y_min;
                                            P2.X = (int)x_max;
                                            P2.Y = (int)y_max;
                                            Cv2.Rectangle(mat_drawBox, P1, P2, new Scalar(0, 255, 0), 5);
                                            rectangle.X = (int)x_min;
                                            rectangle.Y = (int)y_min;
                                            rectangle.Width = (int)(x_max - x_min);
                                            rectangle.Height = (int)(y_max - y_min);

                                            check_detection = 1;
                                        }
                                    }
                                }
                            }
                            // Detection結果表示・保存
                            mat_drawBox.SaveImage(path_base + "\\detection" + "\\" + name_dir + ".bmp");
                            bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_drawBox);
                            Invoke((MethodInvoker)delegate
                            {
                                pictureBox_detection.Image = bitmap_bitch;
                                pictureBox_detection.Refresh();
                            });
                            label1.BackColor = Color.White;

                            // 舌検出領域でcrop
                            label2.BackColor = Color.Red;
                            for (int i = P1.Y; i < P2.Y; i++)
                            {
                                for (int j = P1.X; j < P2.X; j++)
                                {
                                    Vec3b pix = mat_input.At<Vec3b>(i, j);
                                    mat_cropped.Set<Vec3b>(i, j, pix);
                                }
                            }
                            mat_cropped.SaveImage(path_base + "\\cropped" + "\\" + name_dir + ".bmp");

                            // 舌検出領域でresize
                            OpenCvSharp.Size size_roi = new OpenCvSharp.Size();
                            size_roi.Height = rectangle.Height;
                            size_roi.Width = rectangle.Width;
                            roi = new Rect(P1, size_roi);

                            // 20210201
                            //var mat_input2 = mat_input.Clone();
                            //mat_input2 = mat_input2 * 1.5;

                            mat_roisize = mat_input.Clone(roi);
                            Cv2.Resize(mat_roisize, mat_roi, mat_roi256.Size());
                            mat_roi.SaveImage(path_base + "\\cropresized" + "\\" + name_dir + ".bmp");

                            bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_roi);
                            Invoke((MethodInvoker)delegate
                            {
                                pictureBox_cropResized.Image = bitmap_bitch;
                                pictureBox_cropResized.Refresh();
                            });
                            label2.BackColor = Color.White;

                            // Segmenation
                            label3.BackColor = Color.Red;

                            byte_inputSegmentation = Bitmap2Byte(bitmap_bitch);
                            Thread.Sleep(1000);
                            modelFile = DownloadDefaultModel(path_base);
                            ii = 0;
                            using (var graph = new TFGraph())
                            {
                                var model = File.ReadAllBytes(modelFile);
                                graph.Import(model, "");

                                using (var session = new TFSession(graph))
                                {
                                    var tensor = ImageUtil2.CreateTensorFromImageFile(byte_inputSegmentation);
                                    var runner = session.GetRunner();
                                    runner
                                    //.AddInput(graph["generator/input_image"][0], tensor)
                                    //.Fetch(graph["generator/prediction"][0]);

                                    .AddInput(graph["input_image"][0], tensor)
                                    .Fetch(graph["generator1/decoder_1/Tanh"][0]);

                                    var output = runner.Run();
                                    float[,,,] resultfloat = (float[,,,])output[0].GetValue(jagged: false);

                                    for (int p = 0; p < 256; p++)
                                    {
                                        for (int q = 0; q < 256; q++)
                                        {
                                            float check = resultfloat[0, p, q, 0];
                                            if (check < 0)
                                            {
                                                mask[ii] = 0;
                                            }
                                            else
                                            {
                                                mask[ii] = 255;
                                            }
                                            ii++;
                                        }
                                    }
                                }
                            }
                            GC.Collect();
                            Thread.Sleep(1000);

                            // segmentation結果表示・保存
                            bitmap_bitch = ToGrayBitmap(mask, 256, 256);
                            mat_output = OpenCvSharp.Extensions.BitmapConverter.ToMat(bitmap_bitch);
                            bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_output);
                            Invoke((MethodInvoker)delegate
                            {
                                pictureBox_output.Image = bitmap_bitch;
                                pictureBox_output.Refresh();
                            });
                            label3.BackColor = Color.White;
                            label4.BackColor = Color.Red;
                            mat_output.SaveImage(path_base + "\\output256" + "\\" + name_dir + ".bmp");

                            // 後処理（領域拡張法）でノイズ除去　iteration 2
                            try
                            {
                                RemoveSmallRegion(path_base + "\\output256" + "\\" + name_dir + ".bmp", path_base + "\\output_changed1" + "\\" + name_dir + ".bmp", 500, 1, 1);
                                RemoveSmallRegion(path_base + "\\output_changed1" + "\\" + name_dir + ".bmp", path_base + "\\output_changed2" + "\\" + name_dir + ".bmp", 500, 0, 0);
                            }
                            catch
                            {
                                MessageBox.Show("Error: Unable to reprocess! Please check is there [RemoveSmallRegionDLL.dll] file in floder?", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                break;
                            }
                            mat_outputSRG = new Mat(path_base + "\\output_changed2" + "\\" + name_dir + ".bmp", ImreadModes.GrayScale);
                            bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_outputSRG);
                            Invoke((MethodInvoker)delegate
                            {
                                pictureBox_outputSRG.Image = bitmap_bitch;
                                pictureBox_outputSRG.Refresh();
                            });
                            label4.BackColor = Color.White;
                            label5.BackColor = Color.Red;

                            // バウンディングボックスのサイズに戻す
                            Cv2.Resize(mat_outputSRG, mat_outputChanged, mat_roisize.Size());
                            mat_outputChanged.SaveImage(path_base + "\\output_resized" + "\\" + name_dir + ".bmp");

                            // 入力と同じサイズでマスクを作成(なんかたまに不安定．resizeで微妙にサイズ変わってrange overかも)
                            // 気休め
                            Thread.Sleep(100);
                            GC.Collect();

                            mat_mask = new Mat(mat_input.Size(), MatType.CV_8UC1, 0);
                            var y_mask = 0;
                            for (int y = P1.Y; y < P2.Y; y++)
                            {
                                if (y_mask >= mat_outputChanged.Height)
                                    break;

                                var x_mask = 0;
                                for (int x = P1.X; x < P2.X; x++)
                                {
                                    if (x_mask >= mat_outputChanged.Width)
                                        break;

                                    int pix = mat_outputChanged.At<int>(y_mask, x_mask);
                                    mat_mask.Set<int>(y, x, pix);
                                    x_mask++;
                                }
                                y_mask++;
                            }
                            mmp = 0;
                            Cv2.Resize(mat_mask, mat_mask, mat_input.Size());
                            mat_mask.SaveImage(path_base + "\\mask" + "\\" + name_dir + ".bmp");

                            // ノイズ処理
                            try
                            {
                                RemoveSmallRegion(path_base + "\\mask" + "\\" + name_dir + ".bmp", path_base + "\\mask_changed1" + "\\" + name_dir + ".bmp", 500, 1, 1);
                                RemoveSmallRegion(path_base + "\\mask_changed1" + "\\" + name_dir + ".bmp", path_base + "\\mask_changed2" + "\\" + name_dir + ".bmp", 500, 0, 0);
                            }
                            catch
                            {
                                MessageBox.Show("Error: Unable to reprocess! Please check is there [RemoveSmallRegionDLL.dll] file in floder?", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                break;
                            }
                            mat_maskSRG = new Mat(path_base + "\\mask_changed2" + "\\" + name_dir + ".bmp", ImreadModes.GrayScale);
                            Cv2.Threshold(mat_maskSRG, mat_maskSRG, 128, 255, ThresholdTypes.Binary);

                            // 2値マスクの最終結果
                            mat_maskSRG.SaveImage(path_base + "\\mask_final" + "\\" + name_dir + ".bmp");
                            bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_maskSRG);
                            Invoke((MethodInvoker)delegate
                            {
                                pictureBox_maskSRG.Image = bitmap_bitch;
                                pictureBox_maskSRG.Refresh();
                            });
                            label5.BackColor = Color.White;

                            // 元画像とマスクを合わせて，舌領域を抜き出す　
                            // todo: opencv等によるマスク処理と領域計算へ
                            label6.BackColor = Color.Red;
                            mat_extraction = mat_input.Clone();
                            areaCount = 0;
                            for (int i = 0; i < mat_input.Height; i++)
                            {
                                for (int j = 0; j < mat_input.Width; j++)
                                {
                                    Vec3b pix = mat_extraction.At<Vec3b>(i, j);
                                    if (mat_maskSRG.At<int>(i, j) == 0)
                                    {
                                        pix[0] = (byte)(255);
                                        pix[1] = (byte)(255);
                                        pix[2] = (byte)(255);
                                        mat_extraction.Set<Vec3b>(i, j, pix);
                                    }
                                    else
                                    {
                                        pix[0] = (byte)(mat_extraction.At<Vec3b>(i, j).Item0);
                                        pix[1] = (byte)(mat_extraction.At<Vec3b>(i, j).Item1);
                                        pix[2] = (byte)(mat_extraction.At<Vec3b>(i, j).Item2);
                                        mat_extraction.Set<Vec3b>(i, j, pix);
                                        areaCount++;
                                    }
                                }
                            }
                            mat_extraction.SaveImage(path_base + "\\extraction" + "\\" + name_dir + ".bmp");
                            bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_extraction);
                            Invoke((MethodInvoker)delegate
                            {
                                pictureBox_extraction.Image = bitmap_bitch;
                                pictureBox_extraction.Refresh();
                            });
                            label6.BackColor = Color.White;

                            /// 5点クリックによる色抽出処理
                            var path_colorMatrixXYZ = "xyz.txt";

                            // 色抽出処理のクラス
                            ColorExtractor ce = new ColorExtractor();

                            // マスクされた舌領域画像の作成
                            Mat mat_finalMask = mat_maskSRG.Clone();
                            Cv2.ImWrite(path_base + "\\final_mask" + "\\" + name_dir + ".bmp", mat_finalMask);

                            Mat mat_maskedImg = new Mat();
                            mat_input.CopyTo(mat_maskedImg, mat_finalMask);

                            // 5点クリック法（2010石川）
                            List<OpenCvSharp.Point> list_5points_3 = ce.Get5points(mat_finalMask, ColorExtractor.FivePointMethod.Method3);

                            // 5点座標を入力画像，校正画像と同じディレクトリに保存
                            var path_infocsv = path_img_dirs[b] + "\\Info.csv";
                            FileStream CSV_file = File.Open(path_infocsv, FileMode.OpenOrCreate, FileAccess.Write);
                            CSV_file.Seek(0, SeekOrigin.Begin);
                            CSV_file.SetLength(0);
                            CSV_file.Close();

                            Console.WriteLine(path_infocsv);

                            StreamWriter CSV_data = new StreamWriter(path_infocsv);
                            CSV_data.WriteLine(Path.GetFileName(path_img));
                            foreach (var item in list_5points_3)
                            {
                                string str = item.X + "," + item.Y;
                                CSV_data.WriteLine(str);
                            }
                            CSV_data.Close();

                            // 8領域の取得
                            List<OpenCvSharp.Point> list_8area_3 = ce.Get8area(list_5points_3);

                            // 領域の表示
                            var mat_areaDicision = ce.ShowResult(mat_input.Clone(), list_5points_3, list_8area_3);

                            mat_areaDicision.SaveImage(path_base + "\\autoAreaDecision" + "\\" + name_dir + ".bmp");
                            bitmap_bitch = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat_areaDicision);
                            Invoke((MethodInvoker)delegate
                            {
                                pictureBox_last.Image = bitmap_bitch;
                                pictureBox_last.Refresh();
                            });
                            label7.BackColor = Color.White;

                            //csvにboundingbox情報を保存
                            sw = new StreamWriter(this.path_infocsv, true, System.Text.Encoding.GetEncoding("shift_jis"));
                            sw.WriteLine(
                                    path_img.Substring(6) + ","
                                    + P1.X.ToString() + "," + P1.Y.ToString() + ","
                                    + P2.X.ToString() + "," + P2.Y.ToString() + ","
                                    + Math.Abs(P1.X - P2.X).ToString() + "," + Math.Abs(P1.Y - P2.Y).ToString() + ","
                                    + areaCount.ToString()
                                    );
                            sw.Close();

                            // 処理log
                            label8.BackColor = Color.Red;
                            time = DateTime.Now.ToLocalTime().ToString();
                            File.AppendAllText("Log.txt ", time + " " + "\\" + name_dir + ".bmp" + " Done!\n");
                            Thread.Sleep(100);

                            // 破棄
                            GC.Collect();
                        }
                    }
                }

                // 色抽出のためproclist
                sw_ = new StreamWriter(path_procList, true);
                sw_.WriteLine("END");
                sw_.Close();

                // 最後の日付のデータを監視し，更新され次第，tongueanalysisを閉じる
                var fs2 = File.Open(path_procList, FileMode.Open, FileAccess.Read);
                var sr = new StreamReader(fs2);
                fs2.Seek(0, SeekOrigin.Begin);
                string line = "";
                string line_l = "";
                var path_watchedFile = "";
                while ((line = sr.ReadLine()) != null)
                {
                    if (line != "END")
                    {
                        line_l = line;
                        continue;
                    }
                    Console.WriteLine("Last folder:");
                    var path_watchedFolder = Directory.GetCurrentDirectory() + "\\input\\" + line_l;
                    var path_img_dirs = Directory.GetDirectories(path_watchedFolder).Where(n => !n.Contains("校正")).ToList();
                    path_watchedFolder = path_img_dirs[path_img_dirs.Count() - 1];
                    path_watchedFile = path_watchedFolder + "\\" + "analysis.csv";
                    Console.WriteLine("Watching:" + path_watchedFile);
                }
                sr.Close();
                fs2.Close();

                // proclist最後のファイルについて，analysis.csvの更新を非同期監視
                System.IO.FileSystemWatcher watcher = new System.IO.FileSystemWatcher();
                watcher.Path = Path.GetDirectoryName(path_watchedFile);
                watcher.NotifyFilter = (System.IO.NotifyFilters.LastAccess
                                        | System.IO.NotifyFilters.LastWrite
                                        | System.IO.NotifyFilters.FileName
                                        | System.IO.NotifyFilters.DirectoryName);
                watcher.Filter = "analysis.csv";
                //UIのスレッドにマーシャリングする
                //コンソールアプリケーションでの使用では必要ない
                watcher.SynchronizingObject = this;
                //イベントハンドラの追加
                watcher.Changed += new System.IO.FileSystemEventHandler(watcher_Changed);
                watcher.Created += new System.IO.FileSystemEventHandler(watcher_Changed);
                watcher.Deleted += new System.IO.FileSystemEventHandler(watcher_Changed);
                watcher.Renamed += new System.IO.RenamedEventHandler(watcher_Renamed);

                //監視を開始する
                watcher.EnableRaisingEvents = true;
                Console.WriteLine("監視を開始しました。");

                // TongueAnalysis_bin2.3を動かす
                var proc = Process.Start(@"TongueAnalysis_bin2.3\TongueAnalysis.exe");

                while(true)
                {
                    if (IsLastFileChanged)
                    {
                        watcher.EnableRaisingEvents = false;
                        watcher.Dispose();
                        proc.Kill();
                        break;
                    }
                    else
                    {
                        proc.WaitForExit(100);
                    }
                }

                // 生成されたファイルをわかりやすい場所(exeの親dir)にコピー
                var path_finaloutputDir = @"..\Output";
                if (!Directory.Exists(path_finaloutputDir))
                    Directory.CreateDirectory(path_finaloutputDir);
                else
                {
                    Console.WriteLine(path_finaloutputDir + "は既に存在します");
                    Directory.Delete(path_finaloutputDir, true);
                    Directory.CreateDirectory(path_finaloutputDir);
                }
                var _target = new DirectoryInfo("output");
                foreach (FileInfo file in _target.GetFiles())
                {
                    if (file.Name != "analysis.csv")
                        file.CopyTo(path_finaloutputDir + @"\" + file.Name);
                }

                // 形状に関する出力csvと色に関するcsvを合わせる
                var path_infocsv_ = path_infocsv;
                var path_colorcsv_ = @"output\analysis.csv";
                var path_result_ = path_finaloutputDir + @"\AnalysisResult.csv";
                var isFirst = true;
                using (var sr_info = new StreamReader(path_infocsv_))
                using (var sr_color = new StreamReader(path_colorcsv_))
                using (var sw_result = new StreamWriter(path_result_))
                {
                    while ((sr_info.EndOfStream == false) && (sr_color.EndOfStream == false))
                    {
                        string line_info = sr_info.ReadLine();
                        line_info = line_info.Substring(line_info.IndexOf(",") + 1);
                        Console.WriteLine(line_info);
                        string line_color = sr_color.ReadLine();
                        Console.WriteLine(line_color);


                        string line_result = line_color + line_info;

                        if (isFirst)
                        {
                            var header =
                            "date,time-case,image,L1,a1,b1,L2,a2,b2,L3,a3,b3,L4,a4,b4," +
                            "top left X,top left Y,bottom right X,bottom right Y,Width,Height,Area";
                            sw_result.WriteLine(header);


                            isFirst = false;
                            continue;
                        }

                        sw_result.WriteLine(line_result);
                    }
                    sw_result.Close();
                    sr_info.Close();
                    sr_color.Close();
                }



                // 終了処理
                GC.Collect();
                MessageBox.Show("Finished!");

                Invoke((MethodInvoker)delegate
                {
                    button_start.Enabled = true;
                    button_pause.Enabled = false;
                    label_processingFileName.Text = "Processing File: None";
                });
            }
        }

        //イベントハンドラ
        private void watcher_Changed(System.Object source,
            System.IO.FileSystemEventArgs e)
        {
            switch (e.ChangeType)
            {
                case System.IO.WatcherChangeTypes.Changed:
                    Console.WriteLine(
                        "ファイル 「" + e.FullPath + "」が変更されました。");
                    IsLastFileChanged = true;
                    break;
                case System.IO.WatcherChangeTypes.Created:
                    Console.WriteLine(
                        "ファイル 「" + e.FullPath + "」が作成されました。");
                    break;
                case System.IO.WatcherChangeTypes.Deleted:
                    Console.WriteLine(
                        "ファイル 「" + e.FullPath + "」が削除されました。");
                    break;
            }
        }

        private void watcher_Renamed(System.Object source,
            System.IO.RenamedEventArgs e)
        {
            Console.WriteLine(
                "ファイル 「" + e.FullPath + "」の名前が変更されました。");
        }

        private void Button_pause_Click(object sender, EventArgs e)
        {
            if (button_pause.Text == "Pause")
            {
                manualReset.Reset();
                button_pause.Text = "Continue";
            }
            else
            {
                manualReset.Set();
                button_pause.Text = "Pause";
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {

        }
    }
}