using HalconDotNet;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace DH.Commons.Enums
{
    public class OpenCVEngineTool : IDisposable
    {
        public void Dispose()
        {
            throw new NotImplementedException();
        }


    }


    public static class OpenCVHelper
    {
        [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
        public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);


        public static byte[] MatToBytes(this Mat image)
        {
            if (image != null && image.Data != IntPtr.Zero)
            {
                int size = (int)(image.Total() * image.ElemSize());
                byte[] bytes = new byte[size];
                Marshal.Copy(image.Data, bytes, 0, size);
                return bytes;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 获取水平拼接图片
        /// </summary>
        /// <param name="map1"></param>
        /// <param name="map2"></param>
        /// <returns></returns>
        public static Bitmap GetHConcatImage(Bitmap map1, Bitmap map2)
        {
            var mat1 = map1.ToMat();
            var mat2 = map2.ToMat();
            //var maxChannel = Math.Max(mat1.Channels(), mat2.Channels());
            //var maxType = Math.Max(mat1.Type(), mat2.Type());

            //转通道数
            mat1 = mat1.CvtColor(ColorConversionCodes.GRAY2BGRA);
            //转位深
            mat1.ConvertTo(mat1, mat2.Type());
            Mat resMat = new Mat(mat1.Height, mat1.Width, mat2.Type(), new Scalar(0));
            Cv2.HConcat(mat1, mat2, resMat);
            mat1.Dispose();
            mat2.Dispose();
            return resMat.ToBitmap();
        }

        public static Mat To3Channels(this Mat img)
        {
            if (img == null)
                return null;
            Mat resMat = new Mat(img.Rows, img.Cols, MatType.CV_8UC3);
            Mat[] channels = new Mat[3]; ;
            for (int i = 0; i < 3; i++)
            {
                channels[i] = img;
            }
            Cv2.Merge(channels, resMat);
            img.Dispose();
            img = null;
            return resMat;
        }
        /// <summary>
        /// 把OpenCV图像转换到Halcon图像
        /// </summary>
        /// <param name="mImage">OpenCV图像_Mat</param>
        /// <returns>Halcon图像_HObject</returns>
        public static HObject MatToHImage(Mat mImage)
        {
            try
            {
                HObject hImage;
                int matChannels = 0;   // 通道数
                Type matType = null;
                uint width, height;   // 宽,高
                width = height = 0;  // 宽,高初始化

                // 获取通道数
                matChannels = mImage.Channels();
                if (matChannels == 0)
                {
                    return null;
                }
                if (matChannels == 1)        // 单通道
                //if (true)        // 单通道
                {
                    IntPtr ptr;      // 灰度图通道
                    Mat[] mats = mImage.Split();

                    // 改自:Mat.GetImagePointer1(mImage, out ptr, out matType, out width, out height);    // ptr=2157902018096    cType=byte    width=830    height=822
                    ptr = mats[0].Data;          // 取灰度图值
                    matType = mImage.GetType();  // byte
                    height = (uint)mImage.Rows;        // 高
                    width = (uint)mImage.Cols;         // 宽

                    // 改自:hImage = new HObject(new OpenCvSharp.Size(width, height), MatType.CV_8UC1, newScalar(0));
                    byte[] dataGrayScaleImage = new byte[width * height];      //Mat dataGrayScaleImage = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_8UC1);

                    unsafe
                    {
                        fixed (byte* ptrdata = dataGrayScaleImage)
                        {

                            #region 按行复制
                            //for (int i = 0; i < height; i++)
                            //{
                            //    CopyMemory((IntPtr)(ptrdata + width * i), new IntPtr((long)ptr + width *i), width);
                            //}

                            #endregion

                            CopyMemory((IntPtr)ptrdata, new IntPtr((long)ptr), width * height);
                            HOperatorSet.GenImage1(out hImage, "byte", width, height, (IntPtr)ptrdata);

                        }
                    }
                    return hImage;
                }
                else if (matChannels == 3)   // 三通道
                {
                    IntPtr ptrRed;    // R通道图
                    IntPtr ptrGreen;  // G通道图
                    IntPtr ptrBlue;   // B通道图
                    Mat[] mats = mImage.Split();

                    ptrRed = mats[0].Data;    // 取R通道值
                    ptrGreen = mats[1].Data;  // 取G通道值
                    ptrBlue = mats[2].Data;   // 取B通道值
                    matType = mImage.GetType();  // 类型
                    height = (uint)mImage.Rows;        // 高
                    width = (uint)mImage.Cols;         // 宽

                    // 改自:hImage = new HObject(new OpenCvSharp.Size(width, height), MatType.CV_8UC1, new Scalar(0));
                    byte[] dataRed = new byte[width * height];      //Mat dataGrayScaleImage = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_8UC1);
                    byte[] dataGreen = new byte[width * height];
                    byte[] dataBlue = new byte[width * height];

                    unsafe
                    {
                        fixed (byte* ptrdataRed = dataRed, ptrdataGreen = dataGreen, ptrdataBlue = dataBlue)
                        {

                            #region 按行复制
                            //HImage himg = new HImage("byte", width, height, (IntPtr)ptrdataRed);
                            //for (int i = 0; i < height; i++)
                            //{
                            //     CopyMemory((IntPtr)(ptrdataRed + width * i), new IntPtr((long)ptrRed +width * i), width);
                            //     CopyMemory((IntPtr)(ptrdataGreen + width * i), new IntPtr((long)ptrGreen+ width * i), width);
                            //     CopyMemory((IntPtr)(ptrdataBlue + width * i), new IntPtr((long)ptrBlue +width * i), width);
                            //}

                            #endregion

                            CopyMemory((IntPtr)ptrdataRed, new IntPtr((long)ptrRed), width* height);        // 复制R通道
                            CopyMemory((IntPtr)ptrdataGreen, new IntPtr((long)ptrGreen), width * height);    // 复制G通道
                            CopyMemory((IntPtr)ptrdataBlue, new IntPtr((long)ptrBlue), width * height);      // 复制B通道
                            HOperatorSet.GenImage3(out hImage, "byte", width, height, (IntPtr)ptrdataRed, (IntPtr)ptrdataGreen, (IntPtr)ptrdataBlue);   // 合成
                        }
                    }
                    return hImage;
                }
                else
                {
                    return null;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }
 
 
 


        public static Mat HImageToMat(this HObject hobj)
        {
            try
            {
                Mat mImage;
                HTuple htChannels;
                HTuple cType = null;
                HTuple width, height;
                width = height = 0;

                HOperatorSet.CountChannels(hobj, out htChannels);

                if (htChannels.Length == 0)
                {
                    return null;
                }
                if (htChannels[0].I == 1)
                {
                    HTuple ptr;
                    HOperatorSet.GetImagePointer1(hobj, out ptr, out cType, out width, out height);
                    mImage = new Mat(height, width, MatType.CV_8UC1, new Scalar(0));

                    unsafe
                    {
                        CopyMemory(mImage.Data, new IntPtr((byte*)ptr.IP), (uint)(width * height));// CopyMemory(要复制到的地址,复制源的地址,复制的长度)
                    }

                    return mImage;
                }
                else if (htChannels[0].I == 3)
                {
                    HTuple ptrRed;
                    HTuple ptrGreen;
                    HTuple ptrBlue;

                    HOperatorSet.GetImagePointer3(hobj, out ptrRed, out ptrGreen, out ptrBlue, out cType, out width, out height);
                    Mat pImageRed = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_8UC1);
                    Mat pImageGreen = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_8UC1);
                    Mat pImageBlue = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_8UC1);
                    mImage = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_8UC3, new Scalar(0, 0, 0));
                    unsafe
                    {
                        CopyMemory(pImageRed.Data, new IntPtr((byte*)ptrRed.IP), (uint)(width * height));        // CopyMemory(要复制到的地址,复制源的地址,复制的长度)_Red
                        CopyMemory(pImageGreen.Data, new IntPtr((byte*)ptrGreen.IP), (uint)(width * height));  // CopyMemory(要复制到的地址,复制源的地址,复制的长度)_Green
                        CopyMemory(pImageBlue.Data, new IntPtr((byte*)ptrBlue.IP), (uint)(width * height));    // CopyMemory(要复制到的地址,复制源的地址,复制的长度)_Blue

                    }
                    Mat[] multi = new Mat[] { pImageBlue, pImageGreen, pImageRed };
                    Cv2.Merge(multi, mImage);
                    pImageRed.Dispose();
                    pImageRed = null;
                    pImageGreen.Dispose();
                    pImageGreen = null;
                    pImageBlue.Dispose();
                    pImageBlue = null;
                    return mImage;
                }
                else
                {
                    return null;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        ///   <summary>
        ///  从内存流中指定位置,读取数据
        ///   </summary>
        ///   <param name="curStream"></param>
        ///   <param name="startPosition"></param>
        ///   <param name="length"></param>
        ///   <returns></returns>
        public static int ReadData(MemoryStream curStream, int startPosition, int length)
        {
            int result = -1;

            byte[] tempData = new byte[length];
            curStream.Position = startPosition;
            curStream.Read(tempData, 0, length);
            result = BitConverter.ToInt32(tempData, 0);

            return result;
        }

        ///   <summary>
        ///  使用byte[]数据,生成三通道 BMP 位图
        ///   </summary>
        ///   <param name="originalImageData"></param>
        ///   <param name="originalWidth"></param>
        ///   <param name="originalHeight"></param>
        ///   <returns></returns>
        public static Bitmap CreateColorBitmap(byte[] originalImageData, int originalWidth, int originalHeight, byte[] color_map)
        {
            // 指定8位格式,即256色
            Bitmap resultBitmap = new Bitmap(originalWidth, originalHeight, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);

            // 将该位图存入内存中
            MemoryStream curImageStream = new MemoryStream();
            resultBitmap.Save(curImageStream, System.Drawing.Imaging.ImageFormat.Bmp);
            curImageStream.Flush();

            // 由于位图数据需要DWORD对齐(4byte倍数),计算需要补位的个数
            int curPadNum = ((originalWidth * 8 + 31) / 32 * 4) - originalWidth;

            // 最终生成的位图数据大小
            int bitmapDataSize = ((originalWidth * 8 + 31) / 32 * 4) * originalHeight;

            // 数据部分相对文件开始偏移,具体可以参考位图文件格式
            int dataOffset = ReadData(curImageStream, 10, 4);


            // 改变调色板,因为默认的调色板是32位彩色的,需要修改为256色的调色板
            int paletteStart = 54;
            int paletteEnd = dataOffset;
            int color = 0;

            for (int i = paletteStart; i < paletteEnd; i += 4)
            {
                byte[] tempColor = new byte[4];
                tempColor[0] = (byte)color;
                tempColor[1] = (byte)color;
                tempColor[2] = (byte)color;
                tempColor[3] = (byte)0;
                color++;

                curImageStream.Position = i;
                curImageStream.Write(tempColor, 0, 4);
            }

            // 最终生成的位图数据,以及大小,高度没有变,宽度需要调整
            byte[] destImageData = new byte[bitmapDataSize];
            int destWidth = originalWidth + curPadNum;

            // 生成最终的位图数据,注意的是,位图数据 从左到右,从下到上,所以需要颠倒
            for (int originalRowIndex = originalHeight - 1; originalRowIndex >= 0; originalRowIndex--)
            {
                int destRowIndex = originalHeight - originalRowIndex - 1;

                for (int dataIndex = 0; dataIndex < originalWidth; dataIndex++)
                {
                    // 同时还要注意,新的位图数据的宽度已经变化destWidth,否则会产生错位
                    destImageData[destRowIndex * destWidth + dataIndex] = originalImageData[originalRowIndex * originalWidth + dataIndex];
                }
            }

            // 将流的Position移到数据段   
            curImageStream.Position = dataOffset;

            // 将新位图数据写入内存中
            curImageStream.Write(destImageData, 0, bitmapDataSize);

            curImageStream.Flush();

            // 将内存中的位图写入Bitmap对象
            resultBitmap = new Bitmap(curImageStream);

            resultBitmap = Convert8to24(resultBitmap, color_map);  // 转为3通道图像

            return resultBitmap;
        }

        // 实现单通道到多通道
        public static Bitmap Convert8to24(this Bitmap bmp, byte[] color_map)
        {

            Rectangle rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height);
            BitmapData bitmapData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);

            //计算实际8位图容量
            int size8 = bitmapData.Stride * bmp.Height;
            byte[] grayValues = new byte[size8];

            //// 申请目标位图的变量,并将其内存区域锁定  
            Bitmap TempBmp = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format24bppRgb);
            BitmapData TempBmpData = TempBmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);


            //// 获取图像参数以及设置24位图信息 
            int stride = TempBmpData.Stride;  // 扫描线的宽度  
            int offset = stride - TempBmp.Width;  // 显示宽度与扫描线宽度的间隙  
            IntPtr iptr = TempBmpData.Scan0;  // 获取bmpData的内存起始位置  
            int scanBytes = stride * TempBmp.Height;// 用stride宽度,表示这是内存区域的大小  

            // 下面把原始的显示大小字节数组转换为内存中实际存放的字节数组  
            byte[] pixelValues = new byte[scanBytes];  //为目标数组分配内存  
            System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, grayValues, 0, size8);

            for (int i = 0; i < bmp.Height; i++)
            {

                for (int j = 0; j < bitmapData.Stride; j++)
                {

                    if (j >= bmp.Width)
                        continue;

                    int indexSrc = i * bitmapData.Stride + j;
                    int realIndex = i * TempBmpData.Stride + j * 3;

                    // color_id:就是预测出来的结果
                    int color_id = (int)grayValues[indexSrc] % 256;

                    if (color_id == 0) // 分割中类别1对应值1,而背景往往为0,因此这里就将背景置为[0, 0, 0]
                    {
                        // 空白
                        pixelValues[realIndex] = 0;
                        pixelValues[realIndex + 1] = 0;
                        pixelValues[realIndex + 2] = 0;
                    }
                    else
                    {
                        // 替换为color_map中的颜色值
                        pixelValues[realIndex] = color_map[color_id * 3];
                        pixelValues[realIndex + 1] = color_map[color_id * 3 + 1];
                        pixelValues[realIndex + 2] = color_map[color_id * 3 + 2];
                    }
                }
            }

            //Parallel.For(0, width * height, i =>
            //     {
            //         int index = (i + 1) % width + widthIn4 * ((i + 1) / width) - 1;

            //         showBitmapBuffer[index * 6] = bitmapBuffer[index * 6] = data[i * 2];
            //         showBitmapBuffer[index * 6 + 1] = bitmapBuffer[index * 6 + 1] = data[i * 2 + 1];
            //     });

            // 用Marshal的Copy方法,将刚才得到的内存字节数组复制到BitmapData中  
            Marshal.Copy(pixelValues, 0, iptr, scanBytes);
            TempBmp.UnlockBits(TempBmpData);  // 解锁内存区域  
            bmp.UnlockBits(bitmapData);
            return TempBmp;
        }


        // 获取伪彩色图的RGB值 -- 同时也是适用于检测框分类颜色
        public static byte[] GetColorMap(int num_classes = 256)
        {
            num_classes += 1;
            byte[] color_map = new byte[num_classes * 3];
            for (int i = 0; i < num_classes; i++)
            {
                int j = 0;
                int lab = i;
                while (lab != 0)
                {
                    color_map[i * 3] |= (byte)(((lab >> 0) & 1) << (7 - j));
                    color_map[i * 3 + 1] |= (byte)(((lab >> 1) & 1) << (7 - j));
                    color_map[i * 3 + 2] |= (byte)(((lab >> 2) & 1) << (7 - j));

                    j += 1;
                    lab >>= 3;
                }
            }

            // 去掉底色
            color_map = color_map.Skip(3).ToArray();
            return color_map;
        }

        // GetGrayMap
        public static byte[] GetGrayMap(int num_classes = 256)
        {
            byte[] color_map = new byte[num_classes];
            for (int i = 0; i < num_classes; i++)
            {
                if (i <= 100)
                    color_map[i] = 0;
                if (i > 100 && i <= 200)
                    color_map[i] = 100;
                if (i > 200)
                    color_map[i] = 255;

            }
            return color_map;
        }
        /// <summary>
        /// 像素点阵转换为bitmap 二值化图
        /// </summary>
        /// <param name="rawValues">byte[]数组</param>
        /// <param name="width">图片的宽度</param>
        /// <param name="height">图片的高度</param>
        /// <returns>bitmap图片</returns>
        public static Bitmap CreateBinaryBitmap(byte[] rawValues, int width, int height)
        {
            Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
            BitmapData bmpData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
            //获取图像参数
            int stride = bmpData.Stride;  // 扫描线的宽度  
            int offset = stride - width;  // 显示宽度与扫描线宽度的间隙  
            IntPtr iptr = bmpData.Scan0;  // 获取bmpData的内存起始位置  
            int scanBytes = stride * height;// 用stride宽度,表示这是内存区域的大小  
                                            //下面把原始的显示大小字节数组转换为内存中实际存放的字节数组
            int posScan = 0, posReal = 0;// 分别设置两个位置指针,指向源数组和目标数组  
            byte[] pixelValues = new byte[scanBytes];  //为目标数组分配内存  
            for (int x = 0; x < height; x++)
            {
                //下面的循环节是模拟行扫描
                for (int y = 0; y < width; y++)
                {
                    pixelValues[posScan++] = rawValues[posReal++] == 0 ? (byte)0 : (byte)255;
                }
                posScan += offset;  //行扫描结束,要将目标位置指针移过那段“间隙”  
            }
            //用Marshal的Copy方法,将刚才得到的内存字节数组复制到BitmapData中
            Marshal.Copy(pixelValues, 0, iptr, scanBytes);
            bmp.UnlockBits(bmpData);  // 解锁内存区域  
                                      //下面的代码是为了修改生成位图的索引表,从伪彩修改为灰度
            ColorPalette tempPalette;
            using (Bitmap tempBmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format8bppIndexed))
            {
                tempPalette = tempBmp.Palette;
            }
            for (int i = 0; i < 256; i++)
            {
                tempPalette.Entries[i] = System.Drawing.Color.FromArgb(i, i, i);
            }

            bmp.Palette = tempPalette;

            return bmp;
        }

        /// <summary>
        /// 像素点阵转换为bitmap灰度图
        /// </summary>
        /// <param name="rawValues">byte[]数组</param>
        /// <param name="width">图片的宽度</param>
        /// <param name="height">图片的高度</param>
        /// <returns>bitmap图片</returns>
        public static Bitmap CreateGrayBitmap(byte[] rawValues, int width, int height)
        {
            Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
            BitmapData bmpData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
            //获取图像参数
            int stride = bmpData.Stride;  // 扫描线的宽度  
            int offset = stride - width;  // 显示宽度与扫描线宽度的间隙  
            IntPtr iptr = bmpData.Scan0;  // 获取bmpData的内存起始位置  
            int scanBytes = stride * height;// 用stride宽度,表示这是内存区域的大小  
                                            //下面把原始的显示大小字节数组转换为内存中实际存放的字节数组
            int posScan = 0, posReal = 0;// 分别设置两个位置指针,指向源数组和目标数组  
            byte[] pixelValues = new byte[scanBytes];  //为目标数组分配内存  
            for (int x = 0; x < height; x++)
            {
                //下面的循环节是模拟行扫描
                for (int y = 0; y < width; y++)
                {
                    pixelValues[posScan++] = rawValues[posReal++];
                }
                posScan += offset;  //行扫描结束,要将目标位置指针移过那段“间隙”  
            }
            //用Marshal的Copy方法,将刚才得到的内存字节数组复制到BitmapData中
            Marshal.Copy(pixelValues, 0, iptr, scanBytes);
            bmp.UnlockBits(bmpData);  // 解锁内存区域  
                                      //下面的代码是为了修改生成位图的索引表,从伪彩修改为灰度
            ColorPalette tempPalette;
            using (Bitmap tempBmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format8bppIndexed))
            {
                tempPalette = tempBmp.Palette;
            }
            for (int i = 0; i < 256; i++)
            {
                tempPalette.Entries[i] = System.Drawing.Color.FromArgb(i, i, i);
            }

            bmp.Palette = tempPalette;

            return bmp;
        }


        //分别基于像素(GetPixel和SetPixel)、基于内存、基于指针这三种方法增强图片对比度。
        // 第一种方法:像素提取法。速度慢 基于像素:400-600ms
        public static Bitmap MethodBaseOnPixel(Bitmap bitmap, int degree)
        {
            Color curColor;
            int grayR, grayG, grayB;

            double Deg = (100.0 + degree) / 100.0;
            for (int i = 0; i < bitmap.Width; i++)
            {
                for (int j = 0; j < bitmap.Height; j++)
                {
                    curColor = bitmap.GetPixel(i, j);
                    grayR = Convert.ToInt32((((curColor.R / 255.0 - 0.5) * Deg + 0.5)) * 255);
                    grayG = Convert.ToInt32((((curColor.G / 255.0 - 0.5) * Deg + 0.5)) * 255);
                    grayB = Convert.ToInt32((((curColor.B / 255.0 - 0.5) * Deg + 0.5)) * 255);
                    if (grayR < 0)
                        grayR = 0;
                    else if (grayR > 255)
                        grayR = 255;

                    if (grayB < 0)
                        grayB = 0;
                    else if (grayB > 255)
                        grayB = 255;

                    if (grayG < 0)
                        grayG = 0;
                    else if (grayG > 255)
                        grayG = 255;

                    bitmap.SetPixel(i, j, Color.FromArgb(grayR, grayG, grayB));
                }
            }

            return bitmap;
        }

        // 第二种方法:基于内存 17-18ms
        public static unsafe Bitmap MethodBaseOnMemory(Bitmap bitmap, int degree)
        {
            if (bitmap == null)
            {
                return null;
            }
            double Deg = (100.0 + degree) / 100.0;

            int width = bitmap.Width;
            int height = bitmap.Height;

            int length = height * 3 * width;
            byte[] RGB = new byte[length];

            BitmapData data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            System.IntPtr Scan0 = data.Scan0;
            System.Runtime.InteropServices.Marshal.Copy(Scan0, RGB, 0, length);

            double gray = 0;
            for (int i = 0; i < RGB.Length; i += 3)
            {
                for (int j = 0; j < 3; j++)
                {
                    gray = (((RGB[i + j] / 255.0 - 0.5) * Deg + 0.5)) * 255.0;
                    if (gray > 255)
                        gray = 255;

                    if (gray < 0)
                        gray = 0;
                    RGB[i + j] = (byte)gray;
                }
            }

            System.Runtime.InteropServices.Marshal.Copy(RGB, 0, Scan0, length);// 此处Copy是之前Copy的逆操作
            bitmap.UnlockBits(data);
            return bitmap;
        }

        //第三种方法:基于指针 20-23ms
        public static unsafe Bitmap MethodBaseOnPtr(Bitmap b, int degree)
        {
            if (b == null)
            {
                return null;
            }
            try
            {
                double num = 0.0;
                double num2 = (100.0 + degree) / 100.0;
                num2 *= num2;
                int width = b.Width;
                int height = b.Height;
                BitmapData bitmapdata = b.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                byte* numPtr = (byte*)bitmapdata.Scan0;

                int offset = bitmapdata.Stride - (width * 3);
                for (int i = 0; i < height; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        for (int k = 0; k < 3; k++)
                        {
                            num = ((((((double)numPtr[k]) / 255.0) - 0.5) * num2) + 0.5) * 255.0;
                            if (num < 0.0)
                            {
                                num = 0.0;
                            }
                            if (num > 255.0)
                            {
                                num = 255.0;
                            }
                            numPtr[k] = (byte)num;
                        }
                        numPtr += 3;

                    }
                    numPtr += offset;
                }
                b.UnlockBits(bitmapdata);
                return b;
            }
            catch
            {
                return b;
            }
        }

        public static double GetRoiArae(Mat src, Rect rect)
        {
            //初始面积为0
            double Area = 0;
            //获取感兴趣区域
            src = src[rect];
            //转为单通道
            Mat gray = src.CvtColor(ColorConversionCodes.BGR2GRAY);
            //二值化
            Mat binary = gray.Threshold(0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary);

            //求轮廓 不连通会有多个闭合区域
            OpenCvSharp.Point[][] contours;
            HierarchyIndex[] hierarchy;
            Cv2.FindContours(binary, out contours, out hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxSimple);
            for (int i = 0; i < contours.Count(); i++)
            {
                //所有面积相加
                Area += Cv2.ContourArea(contours[i], false);
            }
            return Area;
        }
    }
}