EyemManager.cs 11.8 KB
using CodeLibrary;
using OnlineStore.Common;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;

namespace OnlineStore.DeviceLibrary
{
    public class EyemManager
    {
        static EyemManager()
        {
            try
            {
                //int flag = eyemInitNNDetector(".\\darknet\\detect-tiny-label.cfg", ".\\darknet\\detect-tiny-label.weights", 640, 640);
                int flag = eyemInitNNDetector("", ".\\darknet\\best.onnx", 640, 640);
                if (flag != 0)
                {
                    LogUtil.error("EyemManager" + $"初始化模型失败");
                    //return false;
                    //MessageBox.Show("初始化模型失败!", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk);
                }
                LogUtil.info("EyemManager" + $"init ok ,flag="+ flag);
                eyemNNDetectorParams(0.35f, 0.45f);
            }
            catch (Exception ex)
            {
                LogUtil.error(" EyemManager Init error :" + ex.ToString());
            }

            //return true;
        }
        public static bool? CameraTestReel(string cameraName) {

            //Camera._cam.CaptureOnImage(cameraName, out Bitmap bmp);
            var bmp = RobotManager.CameraA.CameraGrabOne();
            if (bmp != null) {
               return ReelCheck(bmp);
            }
            return null;
        }

        public static bool record = false;//是否保存结果图片
        public static bool? ReelCheck(Bitmap a)
        {
            Bitmap b = GetReducedImage(a, 1920, 0);
            a.Dispose();
            var bl = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, b.PixelFormat);
            EyemImage image = new EyemImage();
            image.iWidth = b.Width;
            image.iHeight = b.Height;
            image.iChannels = 4;
            image.iDepth = 0;
            image.ucpImage = bl.Scan0;
            Directory.CreateDirectory("CameraDebug");
            try
            {
                int ipNum;
                BboxContainer bboxes = new BboxContainer();
                eyemNNDetector(image, out ipNum, ref bboxes, out EyemImage tpDstImg);

                if (record)
                {
                    var c = eyemCvtToBitmap(tpDstImg);                    
                    b.Save($"CameraDebug\\{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.jpg");
                    c.Save($"CameraDebug\\{DateTime.Now:yyyy-MM-dd-HH-mm-ss}_m.jpg");
                    c.Dispose();
                }
                eyemImageFree(ref tpDstImg);
                if (ipNum > 0)
                {
                    var reellist = bboxes.bboxes.Take(ipNum).Select(bx => new { X = bx.iXs + bx.iWidth / 2, Y = bx.iYs + bx.iHeight / 2 }).ToList();
                    
                    
                    var has = reellist.FindIndex(rl => {
                        return IsPointInQuadrilateral(new PointF(rl.X, rl.Y), new PointF(577, 734), new PointF(810, 687), new PointF(877, 886), new PointF(701, 993));
                    });

                    LogUtil.info("EyemManager" + $"eyemNNDetector ,ipNum={ipNum},has reel={has >= 0}");
                    return has >= 0;
                }
                else
                {
                    LogUtil.info("EyemManager" + $"eyemNNDetector ,ipNum={ipNum},has reel=false");
                    return false;
                }


            }
            catch (Exception ex)
            {
                LogUtil.error(" EyemManager ReelCheck error :" + ex.ToString());
                return null;
            }
            finally
            {
                b.UnlockBits(bl);
                b.Dispose();
            }
        }

        #region 通用
        //读取图像,支持彩色与多深度
        [DllImport("eyemLib.dll", CharSet = CharSet.None, CallingConvention = CallingConvention.Cdecl)]
        private static extern int eyemImageRead(string filename, int iFlags, out EyemImage tpImage);
        //释放图像资源
        [DllImport("eyemLib.dll", CharSet = CharSet.None, CallingConvention = CallingConvention.Cdecl)]
        private static extern void eyemImageFree(ref EyemImage tpImage);
        #endregion
        #region 项目
        /// <summary>
        /// 初始化检测器
        /// </summary>
        /// <param name="detectorConfigPath">配置文件</param>
        /// <param name="detectorModelPath">模型文件</param>
        /// <returns></returns>
        [DllImport("eyemLib.dll", CharSet = CharSet.None, CallingConvention = CallingConvention.Cdecl)]
        private static extern int eyemInitNNDetector(string detectorConfigPath, string detectorModelPath, int iNetSizew, int iNetSizeh);

        /// <summary>
        /// 目标检测器
        /// </summary>
        /// <param name="tpImage">输入图像</param>
        /// <returns></returns>
        [DllImport("eyemLib.dll", CharSet = CharSet.None, CallingConvention = CallingConvention.Cdecl)]
        private static extern int eyemNNDetector(EyemImage tpImage, out int ipNum, ref BboxContainer container, out EyemImage tpDstImg);
        [DllImport("eyemLib.dll", CharSet = CharSet.None, CallingConvention = CallingConvention.Cdecl)]
        private static extern int eyemNNDetectorParams(float fConfidence, float fNMSThreshold);
        #endregion
        #region 结构体
        //图像信息
        [StructLayout(LayoutKind.Sequential)]
        public struct EyemImage
        {
            public IntPtr ucpImage;                     // 地址
            public int iWidth;                         // 图像内存 x 方向大小
            public int iHeight;                        // 图像内存 y 方向大小
            public int iDepth;                         // 图像位深度(详见说明)
            public int iChannels;                      // 图像通道数
        }
        // 矩形定义
        [StructLayout(LayoutKind.Sequential)]
        public struct EyemRect
        {
            public int iXs;                            // 起始点(左上角) x 坐标
            public int iYs;                            // 起始点(左上角) y 坐标
            public int iWidth;                         // x 方向大小(宽度)
            public int iHeight;                        // y 方向大小(高度)
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct BboxContainer
        {
            //最多支持100个目标
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
            public EyemRect[] bboxes;
        }
        #endregion



        /// <summary> 
        /// 生成缩略图重载方法1,返回缩略图的Image对象 
        /// </summary> 
        /// <param name="width">缩略图的宽度</param> 
        /// <param name="height">缩略图的高度</param> 
        /// <returns>缩略图的Image对象</returns> 
        private static Bitmap GetReducedImage(Bitmap resourceImage, int width, int height)
        {
            if (height == 0)
            {
                var sc = resourceImage.Width / (float)width;
                height = (int)(resourceImage.Height / sc);
            }

            try
            {
                Bitmap data = null;
                //用指定的大小和格式初始化Bitmap类的新实例 
                using (Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb))
                {
                    //从指定的Image对象创建新Graphics对象 
                    using (Graphics graphics = Graphics.FromImage(bitmap))
                    {
                        //清除整个绘图面并以透明背景色填充 
                        //graphics.Clear(Color.Transparent);
                        //在指定位置并且按指定大小绘制原图片对象 
                        graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
                        graphics.DrawImage(resourceImage, new Rectangle(0, 0, width, height));
                    }
                    data = new Bitmap(bitmap);
                }
                return data;
            }
            catch (Exception e)
            {
                throw e;
            }
        }
        public T DeepClone<T>(T _object)
        {
            T dstobject;
            using (MemoryStream mStream = new MemoryStream())
            {
                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(mStream, _object);
                mStream.Seek(0, SeekOrigin.Begin);//指定当前流的位置为流的开头。
                dstobject = (T)bf.Deserialize(mStream);
                mStream.Close();
            }
            return dstobject;
        }

        #region EyemImage转换成Bitmap
        public static unsafe Bitmap eyemCvtToBitmap(EyemImage tpImage)
        {
            if (tpImage.ucpImage == IntPtr.Zero)
                throw new ArgumentNullException("图像不存在");

            if (tpImage.iDepth != 0)
                throw new ArgumentException("图像必须是8位无符号整型");

            PixelFormat format;

            switch (tpImage.iChannels)
            {
                case 1:
                    format = PixelFormat.Format8bppIndexed;
                    break;
                case 3:
                    format = PixelFormat.Format24bppRgb;
                    break;
                case 4:
                    format = PixelFormat.Format32bppArgb;
                    break;
                default:
                    return null;
            }

            Bitmap bitmap = new Bitmap(tpImage.iWidth, tpImage.iHeight, format);

            //对于输出灰度图像
            if (format == PixelFormat.Format8bppIndexed)
            {
                ColorPalette palette = bitmap.Palette;
                for (int i = 0; i < 256; i++)
                {
                    palette.Entries[i] = Color.FromArgb(i, i, i);
                }
                bitmap.Palette = palette;
            }

            //锁定数据区
            BitmapData bd = bitmap.LockBits(new Rectangle(0, 0, tpImage.iWidth, tpImage.iHeight),
                ImageLockMode.WriteOnly, format);

            try
            {
                int pd = ((tpImage.iWidth * tpImage.iChannels) + 3) / 4 * 4;

                long bytesToCopy = tpImage.iWidth * tpImage.iChannels;

                for (int y = 0; y < tpImage.iHeight; y++)
                {
                    long offsetSrc = (y * tpImage.iWidth * tpImage.iChannels);
                    long offsetDst = (y * pd);

                    Buffer.MemoryCopy((byte*)(tpImage.ucpImage.ToPointer()) + offsetSrc, (byte*)(bd.Scan0.ToPointer()) + offsetDst, bytesToCopy, bytesToCopy);
                }
            }
            catch (Exception ex)
            {
                LogUtil.error("EyemManager eyemCvtToBitmap error:" + ex);
            }
            finally
            {
                bitmap.UnlockBits(bd);
            }
            return bitmap;
        }
        #endregion


        public static bool IsPointInQuadrilateral(PointF p0, PointF p1, PointF p2, PointF p3, PointF p4)
        {
            // 判断p0是否在p1p2p3p4所在的平面内
            float d1 = ((p0.X - p1.X) * (p2.Y - p1.Y)) - ((p2.X - p1.X) * (p0.Y - p1.Y));
            float d2 = ((p0.X - p2.X) * (p3.Y - p2.Y)) - ((p3.X - p2.X) * (p0.Y - p2.Y));
            float d3 = ((p0.X - p3.X) * (p4.Y - p3.Y)) - ((p4.X - p3.X) * (p0.Y - p3.Y));
            float d4 = ((p0.X - p4.X) * (p1.Y - p4.Y)) - ((p1.X - p4.X) * (p0.Y - p4.Y));

            // 判断p0是否在p1p2p3p4所构成的四边形内部
            return ((d1 > 0 && d2 > 0 && d3 > 0 && d4 > 0) || (d1 < 0 && d2 < 0 && d3 < 0 && d4 < 0));
        }
    }
}