领胜LDS 键盘AOI检测项目
src/Bro.Common.Device/DeviceBase/CameraBase.cs
@@ -13,6 +13,7 @@
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using static Bro.Common.Helper.EnumHelper;
@@ -70,7 +71,7 @@
        #endregion
        #region 图片相关
        public event Action<CameraBase, Bitmap, string> OnImageUpdated;
        public Action<CameraBase, Bitmap, string> OnImageUpdated;
        public Action<CameraBase, HObject, string> OnHImageOutput { get; set; }
        public event Action OnCameraOpModeChanged;
@@ -140,22 +141,21 @@
        /// </summary>
        /// <returns></returns>
        public abstract void Snapshot();
        public abstract IImageSet Snapshot(IOperationConfig config);
        public virtual ImageSet Snapshot(IOperationConfig config)
        public virtual void InitialImageSet(IImageSet set, ImageSaveOption saveOption)
        {
            OpMode = CameraOpMode.SingleSnapShot;
            ImageSet set = new ImageSet();
            CameraOprerationConfigBase opConfig = config as CameraOprerationConfigBase;
            set.ImageSaveOption.DataFrom(opConfig.ImageSaveOption);
            //CameraOprerationConfigBase opConfig = config as CameraOprerationConfigBase;
            set.ImageSaveOption.DataFrom(saveOption);
            set.IsOriginSaved = !set.ImageSaveOption.IsSaveOriginImage;
            set.IsFitSaved = !set.ImageSaveOption.IsSaveFitImage;
            set.IsAddtionalSaved = !string.IsNullOrWhiteSpace(set.ImageSaveOption.AddtionalSaveType);
            set.OnImageSetTimeout += ImageSet_OnImageSetTimeout;
            _imageSetList.Add(set);
            set.IsAddtionalSaved = string.IsNullOrWhiteSpace(set.ImageSaveOption.AddtionalSaveType);
            //set.OnImageSetTimeout += ImageSet_OnImageSetTimeout;
            return set;
            _imageSetList[set.Id] = set;
        }
        /// <summary>
@@ -171,19 +171,20 @@
        #endregion
        #region 基元处理
        public event Action<CameraBase, List<IShapeElement>, string> OnElementsUpdated;
        public Action<CameraBase, List<IShapeElement>, string> OnElementsUpdated;
        #endregion
        #region 图片缓存/保存
        readonly ConcurrentBag<ImageSet> _imageSetList = new ConcurrentBag<ImageSet>();
        //public readonly ConcurrentBag<IImageSet> _imageSetList = new ConcurrentBag<IImageSet>();
        protected Dictionary<string, IImageSet> _imageSetList = new Dictionary<string, IImageSet>();
        public void NewImageSet(ImageSet set)
        public void NewImageSet(IImageSet set)
        {
            set.IsOriginSaved = !set.ImageSaveOption.IsSaveOriginImage;
            set.IsFitSaved = !set.ImageSaveOption.IsSaveFitImage;
            set.IsAddtionalSaved = !string.IsNullOrWhiteSpace(set.ImageSaveOption.AddtionalSaveType);
            set.OnImageSetTimeout += ImageSet_OnImageSetTimeout;
            _imageSetList.Add(set);
            set.IsAddtionalSaved = string.IsNullOrWhiteSpace(set.ImageSaveOption.AddtionalSaveType);
            //set.OnImageSetTimeout += ImageSet_OnImageSetTimeout;
            _imageSetList[set.Id] = set;
        }
        //public virtual async void UpdateFitElements(List<IShapeElement> eleList, string imgSetId)
@@ -196,12 +197,12 @@
        public virtual async void SaveOriginImage(Bitmap map, Bitmap showImage, string imgSetId)
        {
            Task.Run(() =>
            await Task.Run(() =>
            {
                OnImageUpdated?.Invoke(this, showImage, imgSetId);
            });
            ImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId);
            IImageSet set = _imageSetList[imgSetId];
            if (set == null)
                return;
@@ -224,10 +225,6 @@
                        {
                            LogAsync(DateTime.Now, $"{set.Id}原图保存失败", ex.GetExceptionMessage());
                        }
                        //finally
                        //{
                        //    set.IsOriginSaved = true;
                        //}
                    }
                    set.IsOriginSaved = true;
                    ClearImageSet(set);
@@ -240,7 +237,7 @@
            if (eleList.Count > 0)
                OnElementsUpdated?.BeginInvoke(this, eleList, imgSetId, null, null);
            ImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId);
            IImageSet set = _imageSetList[imgSetId];
            if (set == null)
                return;
@@ -304,7 +301,7 @@
        {
            await Task.Run(() =>
            {
                ImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId);
                IImageSet set = _imageSetList[imgSetId];
                if (set == null)
                    return;
@@ -340,7 +337,7 @@
            });
        }
        private string CheckImageDirectory(string subDir, string prefix)
        protected string CheckImageDirectory(string subDir, string prefix)
        {
            if (string.IsNullOrWhiteSpace(subDir))
            {
@@ -362,7 +359,7 @@
            return imgDir;
        }
        private void SaveImageByNameAndType(Bitmap map, string imageName, ImageFormat imgFormat, string imageDir)
        protected void SaveImageByNameAndType(Bitmap map, string imageName, ImageFormat imgFormat, string imageDir)
        {
            string filePath = Path.Combine(imageDir, $"{imageName}.{imgFormat.ToString().ToLower()}");
            map.Save(filePath, imgFormat);
@@ -370,7 +367,7 @@
        //private Timer clearImageSetTimer = null;
        public void ClearImageSet(ImageSet set)
        public virtual void ClearImageSet(IImageSet set)
        {
            try
            {
@@ -379,9 +376,10 @@
                //    && (!set.ImageSaveOption.IsSaveNGImage || set.IsNGSaved))
                if (set.IsOriginSaved && set.IsFitSaved && set.IsAddtionalSaved)
                {
                    _imageSetList.TryTake(out set);
                    set.Dispose();
                    //LogAsync(DateTime.Now, $"移除图片信息,当前缓存数量:{_imageSetList.Count}", "");
                    _imageSetList.Remove(set.Id);
                    LogAsync(DateTime.Now, $"移除图片信息,当前缓存数量:{_imageSetList.Count}", "");
                }
                //bool flag = false;
@@ -401,7 +399,7 @@
        public void ClearImageSet(string imgSetId)
        {
            ImageSet set = _imageSetList.First(u => u.Id == imgSetId);
            IImageSet set = _imageSetList[imgSetId];
            if (set != null)
                ClearImageSet(set);
        }
@@ -483,6 +481,110 @@
        private System.Threading.Timer clearImageTimer = null;
        #endregion
        #region 图片转换
        [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
        public static extern void CopyMemory(IntPtr dest, IntPtr src, long count);
        protected async void Generate8GrayImageByPointer(int width, int height, IntPtr dataPtr, string imgSetId)
        {
            await Task.Run(() =>
            {
                //************************Mono8 转 Bitmap*******************************
                Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
                Bitmap showImage = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
                ColorPalette cp = bmp.Palette;
                for (int i = 0; i < 256; i++)
                {
                    cp.Entries[i] = Color.FromArgb(i, i, i);
                }
                bmp.Palette = cp;
                ColorPalette cp1 = showImage.Palette;
                for (int i = 0; i < 256; i++)
                {
                    cp1.Entries[i] = Color.FromArgb(i, i, i);
                }
                showImage.Palette = cp1;
                long[] ptr = new long[3];
                Rectangle rect = new Rectangle(0, 0, width, height);
                BitmapData bitmapData = bmp.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
                BitmapData showImageData = showImage.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
                int PixelSize = Bitmap.GetPixelFormatSize(bitmapData.PixelFormat) / 8;
                if (width % 4 == 0)
                {
                    CopyMemory(bitmapData.Scan0, dataPtr, width * height * PixelSize);
                    CopyMemory(showImageData.Scan0, dataPtr, width * height * PixelSize);
                }
                else
                {
                    ptr[0] = bitmapData.Scan0.ToInt64();
                    ptr[1] = showImageData.Scan0.ToInt64();
                    ptr[2] = (long)dataPtr;
                    for (int i = 0; i < height - 1; i++)
                    {
                        ptr[2] += width;
                        CopyMemory((IntPtr)ptr[0], (IntPtr)ptr[2], width * PixelSize);
                        CopyMemory((IntPtr)ptr[1], (IntPtr)ptr[2], width * PixelSize);
                        ptr[0] += bitmapData.Stride;
                        ptr[1] += showImageData.Stride;
                    }
                }
                bmp.UnlockBits(bitmapData);
                showImage.UnlockBits(showImageData);
                SaveOriginImage(bmp, showImage, imgSetId);
            });
        }
        protected async Task Generate16GrayImageByPointer(int width, int height, IntPtr dataPtr, string imgSetId)
        {
            await Task.Run(() =>
            {
                //Thread.Sleep(1000);
                int widthIn4 = (int)Math.Ceiling(width / 4.0) * 4;
                Bitmap bmp = new Bitmap(widthIn4, height, PixelFormat.Format48bppRgb);
                Bitmap showImage = new Bitmap(widthIn4, height, PixelFormat.Format48bppRgb);
                Rectangle rect = new Rectangle(0, 0, widthIn4, height);
                BitmapData bitmapData = bmp.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format48bppRgb);
                BitmapData showImageData = showImage.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format48bppRgb);
                unsafe
                {
                    byte* data = (byte*)dataPtr;
                    byte* bitmapBuffer = (byte*)bitmapData.Scan0;
                    byte* showBitmapBuffer = (byte*)showImageData.Scan0;
                    //Parallel.For(0, height, i =>
                    //      {
                    //          Parallel.For(0, width, j =>
                    //            {
                    //                showBitmapBuffer[(i * widthIn4 + j) * 6] = bitmapBuffer[(i * widthIn4 + j) * 6] = data[(i * width + j) * 2];
                    //                showBitmapBuffer[(i * widthIn4 + j) * 6 + 1] = bitmapBuffer[(i * widthIn4 + j) * 6 + 1] = data[(i * width + j) * 2 + 1];
                    //            });
                    //      });
                    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];
                      });
                }
                bmp.UnlockBits(bitmapData);
                showImage.UnlockBits(showImageData);
                SaveOriginImage(bmp, showImage, imgSetId);
            });
        }
        #endregion
    }
    #region Config
@@ -496,7 +598,7 @@
        [Category("取像配置")]
        [Description("曝光")]
        [DisplayName("曝光")]
        public float Exposure
        public virtual float Exposure
        {
            get => exposure;
            set
@@ -515,17 +617,17 @@
        [Category("取像配置")]
        [Description("增益")]
        [DisplayName("增益")]
        public float Gain { get; set; }
        public virtual float Gain { get; set; }
        [Category("取像配置")]
        [Description("曝光修改后等待时间,单位ms。针对部分场景确保曝光修改及时生效。")]
        [DisplayName("曝光等待")]
        public int ExposureWaitTime { get; set; }
        public virtual int ExposureWaitTime { get; set; }
        [Category("取像配置")]
        [Description("拍摄后曝光值。可在一次拍摄后将曝光修改为下次拍摄的曝光值,节约曝光生效时间。")]
        [DisplayName("拍摄后曝光")]
        public float ExposureAfterSnap { get; set; }
        public virtual float ExposureAfterSnap { get; set; }
        [Category("算法配置")]
        [Description("算法路径")]
@@ -581,42 +683,42 @@
        [Category("相机设置")]
        [Description("相机IP地址")]
        [DisplayName("相机IP地址")]
        public string CameraIP { get; set; }
        public virtual string CameraIP { get; set; }
        [Category("相机设置")]
        [Description("上位机IP地址")]
        [DisplayName("上位机IP地址")]
        public string ComputerIP { get; set; }
        public virtual string ComputerIP { get; set; }
        [Category("保存设置")]
        [Description("图片保存目录")]
        [DisplayName("图片保存目录")]
        [Editor(typeof(FoldDialogEditor), typeof(UITypeEditor))]
        public string ImgDirectory { get; set; } = @"../Images";
        public virtual string ImgDirectory { get; set; } = @"../Images";
        [Category("保存设置")]
        [Description("图片保存天数")]
        [DisplayName("图片保存天数")]
        public int SaveImageDayLimit { get; set; } = 0;
        public virtual int SaveImageDayLimit { get; set; } = 0;
        [Category("保存设置")]
        [Description("图片保存默认配置,主要用于硬触发等没有明确操作配置说明的图片保存")]
        [DisplayName("图片保存默认配置")]
        [TypeConverter(typeof(ComplexObjectConvert))]
        [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))]
        public ImageSaveOption ImageSaveOption { get; set; } = new ImageSaveOption();
        public virtual ImageSaveOption ImageSaveOption { get; set; } = new ImageSaveOption();
        [Category("拍摄设置")]
        [Description("默认曝光值,相机开启后就设置该曝光值")]
        [DisplayName("默认曝光值")]
        public float DefaultExposure { get; set; }
        public virtual float DefaultExposure { get; set; }
    }
    #endregion
    #region Converter & Editor
    public class HalconSerialNumConverter : ComboBoxItemTypeConvert
    {
        public override Hashtable GetConvertHash()
        public override Hashtable GetConvertHash(ITypeDescriptorContext context)
        {
            Hashtable table = new Hashtable();
            HTuple deviceList = null;
@@ -654,7 +756,7 @@
    public class CameraDeviceConverter : ComboBoxItemTypeConvert
    {
        public override Hashtable GetConvertHash()
        public override Hashtable GetConvertHash(ITypeDescriptorContext context)
        {
            Hashtable table = new Hashtable();
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
@@ -733,7 +835,29 @@
        }
    }
    public class ImageSet : IDisposable
    public interface IImageSet:IDisposable
    {
        DateTime InitialTime { get; set; }
        string Id { get; }
        HImage HImage { get; set; }
        Bitmap Image { get; set; }
        object SaveLock { get; set; }
        ImageSaveOption ImageSaveOption { get; set; }
        bool IsOriginSaved { get; set; }
        bool IsFitSaved { get; set; }
        bool IsAddtionalSaved { get; set; }
        //event Action<ImageSet> OnImageSetTimeout;
        /// <summary>
        /// 序列化操作的图像信息
        /// </summary>
        string ImageData { get; set; }
    }
    public class ImageSet : IImageSet
    {
        public DateTime InitialTime { get; set; } = DateTime.Now;
@@ -768,31 +892,32 @@
        //public bool IsInvokeSaveNG { get; set; } = false;
        public bool IsAddtionalSaved { get; set; } = false;
        public object SaveLock = new object();
        public object SaveLock { get; set; } = new object();
        public event Action<ImageSet> OnImageSetTimeout;
        private Timer autoDisposeTimer = null;
        public virtual string ImageData { get; set; }
        private void OnAutoDispose(object state)
        //public event Action<ImageSet> OnImageSetTimeout;
        //private Timer autoDisposeTimer = null;
        public void Dispose(object state)
        {
            OnImageSetTimeout?.Invoke(this);
            //OnImageSetTimeout?.Invoke(this);
            //autoDisposeTimer.Change(-1, -1);
            //autoDisposeTimer.Dispose();
            Image?.Dispose();
            Image = null;
        }
        public virtual void Dispose()
        {
            Dispose(null);
        }
        public ImageSet()
        {
            autoDisposeTimer = new Timer(OnAutoDispose, null, -1, 10 * 1000);
        }
        public void Dispose()
        {
            autoDisposeTimer.Change(-1, -1);
            autoDisposeTimer.Dispose();
            Image?.Dispose();
            Image = null;
            HImage?.Dispose();
            HImage = null;
            //autoDisposeTimer = new Timer(Dispose, null, 10 * 1000, -1);
        }
        #endregion
    }