【转】C#使用ESC指令控制POS打印机打印小票
1 1.前言 2 3 4 C#打印小票可以与普通打印机一样,调用PrintDocument实现。也可以发送标注你的ESC指令实现。由于 调用PrintDocument类时,无法操作使用串口或TCP/IP接口连接的pos打印机,并且无法发送控制指令实现pos打印机的切纸、走纸等动作。因此个人建议使用ESC指令进行打印会更通用。 5 6 本类需要调用 ImageProcessor.cs 7 8 9 10 2.POS机打印小票ReceiptHelper 11 using System; 12 13 using System.Collections.Generic; 14 15 using System.Text; 16 17 using System.Runtime.InteropServices; 18 19 using System.Threading; 20 21 using System.Drawing; 22 23 using System.Management; 24 25 using System.IO; 26 27 using LaisonTech.MediaLib; 28 29 using LaisonTech.CommonBLL; 30 31 using Microsoft.Win32.SafeHandles; 32 33 34 35 namespace LaisonTech.MediaLib 36 37 { 38 39 40 41 #region 结构体定义 42 43 44 45 [StructLayout(LayoutKind.Sequential)] 46 47 public struct OVERLAPPED 48 49 { 50 51 int Internal; 52 53 int InternalHigh; 54 55 int Offset; 56 57 int OffSetHigh; 58 59 int hEvent; 60 61 }; 62 63 64 65 [StructLayout(LayoutKind.Sequential)] 66 67 public struct PRINTER_DEFAULTS 68 69 { 70 71 72 73 public int pDatatype; 74 75 76 77 public int pDevMode; 78 79 80 81 public int DesiredAccess; 82 83 84 85 } 86 87 88 89 /// <summary> 90 91 /// 对齐方式 92 93 /// </summary> 94 95 public enum eTextAlignMode 96 97 { 98 99 Left = 0, 100 101 Middle = 1, 102 103 Right = 2 104 105 } 106 107 108 109 #endregion 110 111 112 113 /// <summary> 114 115 /// 小票打印类 116 117 /// 使用方法: 118 119 /// 1 GetPrinterList获取已经安装的所有打印机列表. 120 121 /// Open 打开指定打印机 122 123 /// 2 控制打印机动作、执行打印内容之前,必须先调用StartPrint,准备向打印机发送控制指令 124 125 /// 3 调用SetLeft, SetBold, SetAlignMode, SetFontSize ... ...设置打印参数 126 127 /// 4 PrintText 打印内容.注意:打印该行内容后会自动换行(本类会在该行内容末尾添加一个换行符) 128 129 /// PrintImageFile 或 PrintBitMap打印图片 130 131 /// 5 控制指令和打印内容都发送完毕后,调用 EndPrint执行真正打印动作 132 133 /// 6 退出程序前调用Close 134 135 /// </summary> 136 137 public class ReceiptHelper 138 139 { 140 141 #region 指令定义 142 143 144 145 private static Byte[] Const_Init = new byte[] { 0x1B, 0x40, 146 147 0x20, 0x20, 0x20, 0x0A, 148 149 0x1B, 0x64,0x10}; 150 151 152 153 //设置左边距 154 155 private const string Const_SetLeft = "1D 4C "; 156 157 158 159 160 161 //设置粗体 162 163 private const string Const_SetBold = "1B 45 "; 164 165 private const String Const_Bold_YES = "01"; 166 167 private const String Const_Bold_NO = "00"; 168 169 170 171 172 173 //设置对齐方式 174 175 private const string Const_SetAlign = "1B 61 "; 176 177 private const String Const_Align_Left = "30"; 178 179 private const String Const_Align_Middle = "31"; 180 181 private const String Const_Align_Right = "32"; 182 183 184 185 //设置字体大小,与 SetBigFont 不能同时使用 186 187 private const string Const_SetFontSize = "1D 21 "; 188 189 190 191 //设置是否大字体,等同于 SetFontSize = 2 192 193 //private const String Const_SetBigFontBold = "1B 21 38"; 194 195 //private const String Const_SetBigFontNotBold = "1B 21 30"; 196 197 //private const String Const_SetCancelBigFont = "1B 21 00"; 198 199 200 201 /// <summary> 202 203 /// 打印并走纸 204 205 /// </summary> 206 207 private static Byte[] Const_Cmd_Print = new byte[] { 0x1B, 0x4A, 0x00 }; 208 209 //走纸 210 211 private const string Const_FeedForward = "1B 4A "; 212 213 private const string Const_FeedBack = "1B 6A "; 214 215 216 217 //切纸 218 219 private static Byte[] Const_SetCut = new byte[] { 0x1D, 0x56, 0x30}; 220 221 222 223 //查询打印机状态 224 225 private static Byte[] Const_QueryID = new byte[] { 0x1D, 0x67, 0x61}; 226 227 228 229 //回复帧以 ID 开头 230 231 private static String Const_ResponseQueryID = "ID"; 232 233 234 235 /// <summary> 236 237 /// 设置图标的指令 238 239 /// </summary> 240 241 private static Byte[] Const_SetImageCommand = new Byte[] { 0x1B, 0x2A, 0x21 }; 242 243 244 245 #endregion 246 247 248 249 #region 常量定义 250 251 252 253 /// <summary> 254 255 /// 最大字体大小 256 257 /// </summary> 258 259 public const Int32 Const_MaxFontSize = 8; 260 261 /// <summary> 262 263 /// 最大走纸距离 264 265 /// </summary> 266 267 public const Int32 Const_MaxFeedLength = 5000; 268 269 270 271 /// <summary> 272 273 /// 最大高宽 274 275 /// </summary> 276 277 public const Int32 Const_MaxImageLength = 480; 278 279 280 281 /// <summary> 282 283 /// 每次通信最多打印的行数 284 285 /// </summary> 286 287 public const Int32 Const_OncePrintRowCount = 24; 288 289 290 291 public const Int32 Const_BrightnessGate = 100; 292 293 294 295 /// <summary> 296 297 /// 无效句柄 298 299 /// </summary> 300 301 public const Int32 Const_InvalidHandle = -1; 302 303 #endregion 304 305 306 307 #region 私有成员 308 309 310 311 /// <summary> 312 313 /// 打印机句柄 314 315 /// </summary> 316 317 private int m_Handle = -1; 318 319 320 321 /// <summary> 322 323 /// 是否已经初始化 324 325 /// </summary> 326 327 private Boolean m_Inited = false; 328 329 330 331 332 333 #endregion 334 335 336 337 #region 私有函数 338 339 340 341 [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] 342 343 public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, 344 345 out Int32 hPrinter, IntPtr pd); 346 347 348 349 [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] 350 351 public static extern bool StartDocPrinter(Int32 hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di); 352 353 354 355 [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] 356 357 public static extern bool EndDocPrinter(Int32 hPrinter); 358 359 360 361 [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] 362 363 public static extern bool StartPagePrinter(Int32 hPrinter); 364 365 366 367 [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] 368 369 public static extern bool EndPagePrinter(Int32 hPrinter); 370 371 372 373 [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] 374 375 public static extern bool WritePrinter(Int32 hPrinter, Byte[] pBytes, Int32 dwCount, out Int32 dwWritten); 376 377 378 379 [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] 380 381 public static extern bool ClosePrinter(Int32 hPrinter); 382 383 384 385 386 387 /// <summary> 388 389 /// 发送指令 390 391 /// </summary> 392 393 /// <param name="cmd"></param> 394 395 /// <returns></returns> 396 397 private Boolean SendCommand(Byte[] cmd) 398 399 { 400 401 if (m_Handle == Const_InvalidHandle || cmd == null || cmd.Length < 2) 402 403 { 404 405 return false; 406 407 } 408 409 410 411 int writelen = 0; 412 413 Boolean bl = WritePrinter(m_Handle, cmd, cmd.Length, out writelen); 414 415 416 417 if (!bl) return false; 418 419 return (writelen >= cmd.Length); 420 421 } 422 423 424 425 /// <summary> 426 427 /// 发送文本格式的指令 428 429 /// </summary> 430 431 /// <param name="cmd"></param> 432 433 /// <returns></returns> 434 435 private Boolean SendCommand(String hexstrcmd) 436 437 { 438 439 if (m_Handle == Const_InvalidHandle || hexstrcmd == null || hexstrcmd.Length < 4) 440 441 { 442 443 return false; 444 445 } 446 447 448 449 byte[] mybyte = null; 450 451 Boolean bl = DataFormatProcessor.HexStringToBytes(hexstrcmd, out mybyte); 452 453 bl = SendCommand(mybyte); 454 455 return bl; 456 457 } 458 459 460 461 462 463 #endregion 464 465 466 467 #region 内部处理 - 打印图片 468 469 470 471 /// <summary> 472 473 /// 把图片转换为指令字节,图片最大高宽不能超过480 474 475 /// </summary> 476 477 /// <param name="image"></param> 478 479 /// <param name="bmpbytes"></param> 480 481 /// <returns></returns> 482 483 public static Boolean LoadImage(Bitmap image, 484 485 ref Byte[] bitarray,ref Int32 datawidth,ref Int32 dataheight) 486 487 { 488 489 Int32 newwidth = 0; 490 491 Int32 newheight = 0; 492 493 Bitmap destimage = image; 494 495 Boolean bl = false; 496 497 498 499 //如果高度超过范围,或宽度超过范围,需要进行缩小 500 501 if (image.Width > Const_MaxImageLength || image.Height > Const_MaxImageLength) 502 503 { 504 505 //按照高度和宽度,较大的那一边,进行缩放 506 507 if (image.Width > image.Height) 508 509 { 510 511 newwidth = Const_MaxImageLength; 512 513 newheight = (Int32)(image.Height * newwidth / (float)image.Width); 514 515 } 516 517 else 518 519 { 520 521 newheight = Const_MaxImageLength; 522 523 newwidth = (Int32)(newheight * image.Width / (float)image.Height); 524 525 } 526 527 528 529 bl = ImageProcessor.ResizeImage(image, newwidth, newheight, ref destimage); 530 531 } 532 533 534 535 //把数据转换为字节数组 536 537 bl = GetBitArray(image, ref bitarray, ref datawidth, ref dataheight); 538 539 return bl; 540 541 } 542 543 544 545 /// <summary> 546 547 /// 把图片转换为指令字节,图片最大高宽不能超过480 548 549 /// 如果图片的高度不是24的整数倍,则修改为24的整数倍 550 551 /// </summary> 552 553 /// <param name="image"></param> 554 555 /// <param name="bmpbytes"></param> 556 557 /// <returns></returns> 558 559 public static Boolean LoadImageFromFile(String imagefilename, ref Byte[] bmpbytes, 560 561 ref Int32 width, ref Int32 height) 562 563 { 564 565 Bitmap img = ImageProcessor.LoadBitImage(imagefilename); 566 567 if (img == null) 568 569 { 570 571 return false; 572 573 } 574 575 576 577 Boolean bl = LoadImage(img, ref bmpbytes, ref width, ref height); 578 579 return bl; 580 581 } 582 583 584 585 /// <summary> 586 587 /// 把图片转换为位图数组,每个字节的每个比特位,对应当前像素 是否需要打印 588 589 /// </summary> 590 591 /// <param name="img"></param> 592 593 /// <param name="allbitary"></param> 594 595 /// <returns></returns> 596 597 public static Boolean GetBitArray(Bitmap img, 598 599 ref Byte[] allbitary, ref Int32 width, ref Int32 height) 600 601 { 602 603 if (img == null) 604 605 { 606 607 return false; 608 609 } 610 611 612 613 //ESC指令格式规定: 614 615 //1 打印图片时,每条指令最多只打印24行;不足24行的,也要用全0填充满数据字节 616 617 //2 打印24行数据时,按照光栅模式纵向获取数据 618 619 // 即先获取所有x=0的点(第0列)转换为3个字节; 620 621 // 再获取所有x=1的点转换为3个字节;...直到获取到最右侧一列的点 622 623 //3 打印完当前24行数据后,再获取后续24行的数据内容,直到所有的数据获取完毕 624 625 626 627 //获取亮度数组 628 629 Boolean[] briary = null; 630 631 Boolean bl = ImageProcessor.ToBooleanArray(img, Const_BrightnessGate, ref briary); 632 633 if (!bl) 634 635 { 636 637 return false; 638 639 } 640 641 642 643 height = img.Height;//如果图像高度不是24整数倍,设置为24的整数倍 644 645 if (height % Const_OncePrintRowCount != 0) 646 647 { 648 649 height = height + Const_OncePrintRowCount - height % Const_OncePrintRowCount; 650 651 } 652 653 654 655 width = img.Width;//如果图像宽度不是8的整数倍,设置为8的整数倍 656 657 if (width % 8 != 0) 658 659 { 660 661 width = width + 8 - width % 8; 662 663 } 664 665 666 667 Int32 bytelen = height * width / 8;//每个像素对应1个比特位,因此总字节数=像素位数/8 668 669 670 671 allbitary = new Byte[bytelen]; 672 673 674 675 Int32 byteidxInCol = 0;//当前列里首个像素,在目标字节数组里的下标 676 677 Int32 byteidx = 0;//当前像素在目标数组里的字节下标 678 679 Int32 bitidx = 0;//当前像素在目标数组里当前字节里的比特位下标 680 681 Int32 pixidxInCol = 0;//当前像素在当前列里的第几个位置 682 683 684 685 Int32 pixidx = 0;//当前像素在原始图片里的下标 686 687 688 689 Int32 rowidx = 0; //当前 处理的像素点所在行,不能超过 图像高度 690 691 Int32 curprocrows = 0;//当前需要处理的行数量 692 693 while (rowidx < height) 694 695 { 696 697 //按照纵向次序,把当前列的24个数据,转换为3个字节 698 699 for (Int32 colidx = 0; colidx < img.Width; ++colidx) 700 701 { 702 703 //如果当前还剩余超过24行没处理,处理24行 704 705 if (rowidx + Const_OncePrintRowCount <= img.Height) 706 707 { 708 709 curprocrows = Const_OncePrintRowCount; 710 711 } 712 713 else 714 715 { 716 717 //已经不足24行,只处理剩余行数 718 719 curprocrows = img.Height - rowidx; 720 721 } 722 723 724 725 pixidxInCol = 0; //本列里从像素0开始处理 726 727 for (Int32 y = rowidx; y < rowidx + curprocrows; ++y) 728 729 { 730 731 //原始图片里像素位置 732 733 pixidx = y * img.Width + colidx; 734 735 736 737 //获取当前像素的亮度值.如果当前像素是黑点,需要把数组里的对应比特位设置为1 738 739 if (briary[pixidx]) 740 741 { 742 743 bitidx = 7 - pixidxInCol % 8;//最高比特位对应首个像素.最低比特位对应末个像素 744 745 byteidx = byteidxInCol + pixidxInCol / 8; //由于最后一段可能不足24行,因此不能使用byteidx++ 746 747 748 749 DataFormatProcessor.SetBitValue(bitidx, true, ref allbitary[byteidx]); 750 751 } 752 753 pixidxInCol++; 754 755 } 756 757 byteidxInCol += 3;//每列固定24个像素,3个字节 758 759 } 760 761 762 763 rowidx += Const_OncePrintRowCount; 764 765 } 766 767 768 769 return true; 770 771 } 772 773 774 775 #endregion 776 777 778 779 #region 公开函数 780 781 782 783 private static ReceiptHelper m_instance = new ReceiptHelper(); 784 785 786 787 /// <summary> 788 789 /// 当前使用的打印机名称 790 791 /// </summary> 792 793 public String PrinterName 794 795 { 796 797 get;private set; 798 799 } 800 801 802 803 /// <summary> 804 805 /// 单件模式 806 807 /// </summary> 808 809 /// <returns></returns> 810 811 public static ReceiptHelper GetInstance() 812 813 { 814 815 return m_instance; 816 817 } 818 819 820 821 /// <summary> 822 823 /// 获取本机安装的所有打印机 824 825 /// </summary> 826 827 /// <returns></returns> 828 829 public static List<String> GetPrinterList() 830 831 { 832 833 List<String> ret = new List<String>(); 834 835 if (PrinterSettings.InstalledPrinters.Count < 1) 836 837 { 838 839 return ret; 840 841 } 842 843 844 845 foreach (String printername in PrinterSettings.InstalledPrinters) 846 847 { 848 849 ret.Add(printername); 850 851 } 852 853 return ret; 854 855 } 856 857 858 859 /// <summary> 860 861 /// 打开打印机 862 863 /// </summary> 864 865 /// <param name="printername"></param> 866 867 /// <returns></returns> 868 869 public Boolean Open(String printername) 870 871 { 872 873 if (m_Inited) 874 875 { 876 877 return true; 878 879 } 880 881 Boolean bl = OpenPrinter(printername.Normalize(), out m_Handle, IntPtr.Zero); 882 883 884 885 m_Inited = (bl && m_Handle != 0); 886 887 return true; 888 889 } 890 891 892 893 /// <summary> 894 895 /// 开始打印,在打印之前必须调用此函数 896 897 /// </summary> 898 899 /// <returns></returns> 900 901 public Boolean StartPrint() 902 903 { 904 905 if (!m_Inited) 906 907 { 908 909 return false; 910 911 } 912 913 DOCINFOA di = new DOCINFOA(); 914 915 di.pDocName = "My C#.NET RAW Document"; 916 917 di.pDataType = "RAW"; 918 919 //Start a document. 920 921 Boolean bl = StartDocPrinter(m_Handle, 1, di); 922 923 if (!bl) 924 925 { 926 927 return false; 928 929 } 930 931 // Start a page. 932 933 bl = StartPagePrinter(m_Handle); 934 935 return bl; 936 937 } 938 939 940 941 /// <summary> 942 943 /// 结束打印,在打印结束之后必须调用此函数 944 945 /// </summary> 946 947 /// <returns></returns> 948 949 public Boolean EndPrint() 950 951 { 952 953 if (!m_Inited) 954 955 { 956 957 return false; 958 959 } 960 961 Boolean bl = EndPagePrinter(m_Handle); 962 963 bl = EndDocPrinter(m_Handle); 964 965 return bl; 966 967 } 968 969 970 971 /// <summary> 972 973 /// 销毁 974 975 /// </summary> 976 977 /// <returns></returns> 978 979 public Boolean Close() 980 981 { 982 983 if (!m_Inited) 984 985 { 986 987 return true; 988 989 } 990 991 m_Inited = false; 992 993 994 995 //关闭设备句柄 996 997 ClosePrinter(m_Handle); 998 999 m_Handle = -1; 1000 1001 return true; 1002 1003 } 1004 1005 1006 1007 /// <summary> 1008 1009 /// 打印文本.在调用本函数之前必须先调用正确的 设置字体、左边距 1010 1011 /// </summary> 1012 1013 /// <param name="content"></param> 1014 1015 /// <returns></returns> 1016 1017 public Boolean PrintText(String content) 1018 1019 { 1020 1021 if (!m_Inited) 1022 1023 { 1024 1025 return false; 1026 1027 } 1028 1029 1030 1031 byte[] bytes = null; 1032 1033 if (content.Length < 1) 1034 1035 { 1036 1037 content = " "; 1038 1039 } 1040 1041 1042 1043 if (content[content.Length - 1] != (char)0x0D && 1044 1045 content[content.Length - 1] != (char)0x0A) 1046 1047 { 1048 1049 content = content + (char)0x0A; 1050 1051 } 1052 1053 1054 1055 bytes = DataFormatProcessor.StringToBytes(content); 1056 1057 bool bl = SendCommand(bytes); 1058 1059 return bl; 1060 1061 } 1062 1063 1064 1065 /// <summary> 1066 1067 /// 设置对齐方式 1068 1069 /// </summary> 1070 1071 /// <param name="left"></param> 1072 1073 /// <returns></returns> 1074 1075 public bool SetAlignMode(eTextAlignMode alignmode) 1076 1077 { 1078 1079 if (!m_Inited) 1080 1081 { 1082 1083 return false; 1084 1085 } 1086 1087 1088 1089 String code = String.Empty; 1090 1091 switch (alignmode) 1092 1093 { 1094 1095 case eTextAlignMode.Left: 1096 1097 code = Const_Align_Left; 1098 1099 break; 1100 1101 case eTextAlignMode.Middle: 1102 1103 code = Const_Align_Middle; 1104 1105 break; 1106 1107 case eTextAlignMode.Right: 1108 1109 code = Const_Align_Right; 1110 1111 break; 1112 1113 default: 1114 1115 code = Const_Align_Left; 1116 1117 break; 1118 1119 } 1120 1121 1122 1123 //注意:先低字节后高字节 1124 1125 string str = Const_SetAlign + code; 1126 1127 bool bl = SendCommand(str); 1128 1129 return bl; 1130 1131 } 1132 1133 1134 1135 /// <summary> 1136 1137 /// 设置左边距 1138 1139 /// </summary> 1140 1141 /// <param name="left"></param> 1142 1143 /// <returns></returns> 1144 1145 public bool SetLeft(int left) 1146 1147 { 1148 1149 if (!m_Inited) 1150 1151 { 1152 1153 return false; 1154 1155 } 1156 1157 1158 1159 //注意:先低字节后高字节 1160 1161 String hexstr = left.ToString("X4"); 1162 1163 string str = Const_SetLeft + hexstr.Substring(2, 2) + hexstr.Substring(0, 2); 1164 1165 bool bl = SendCommand(str); 1166 1167 return bl; 1168 1169 } 1170 1171 1172 1173 /// <summary> 1174 1175 /// 设置粗体 1176 1177 /// </summary> 1178 1179 /// <param name="bold"></param> 1180 1181 /// <returns></returns> 1182 1183 public Boolean SetBold(Boolean bold) 1184 1185 { 1186 1187 if (!m_Inited) 1188 1189 { 1190 1191 return false; 1192 1193 } 1194 1195 1196 1197 //注意:先低字节后高字节 1198 1199 String str = String.Empty; 1200 1201 if (bold) 1202 1203 { 1204 1205 str = Const_SetBold + Const_Bold_YES; 1206 1207 } 1208 1209 else 1210 1211 { 1212 1213 str = Const_SetBold + Const_Bold_NO; 1214 1215 } 1216 1217 bool bl = SendCommand(str); 1218 1219 return bl; 1220 1221 } 1222 1223 1224 1225 /// <summary> 1226 1227 /// 切纸 1228 1229 /// </summary> 1230 1231 /// <returns></returns> 1232 1233 public bool Cut() 1234 1235 { 1236 1237 if (!m_Inited) 1238 1239 { 1240 1241 return false; 1242 1243 } 1244 1245 bool bl = SendCommand(Const_SetCut); 1246 1247 return bl; 1248 1249 } 1250 1251 1252 1253 1254 1255 /// <summary> 1256 1257 /// 打印图片 1258 1259 /// </summary> 1260 1261 /// <param name="bitmap"></param> 1262 1263 /// <returns></returns> 1264 1265 public bool PrintImageFile(String imgfilename) 1266 1267 { 1268 1269 if (!m_Inited) 1270 1271 { 1272 1273 return false; 1274 1275 } 1276 1277 Bitmap img = ImageProcessor.LoadBitImage(imgfilename); 1278 1279 if (img == null) 1280 1281 { 1282 1283 return false; 1284 1285 } 1286 1287 1288 1289 Boolean bl = PrintBitmap(img); 1290 1291 return bl; 1292 1293 } 1294 1295 1296 1297 /// <summary> 1298 1299 /// 打印图片 1300 1301 /// </summary> 1302 1303 /// <param name="bitmap"></param> 1304 1305 /// <returns></returns> 1306 1307 public bool PrintBitmap(Bitmap bitmap) 1308 1309 { 1310 1311 if (!m_Inited) 1312 1313 { 1314 1315 return false; 1316 1317 } 1318 1319 1320 1321 if (bitmap == null || 1322 1323 bitmap.Width > Const_MaxImageLength || 1324 1325 bitmap.Height > Const_MaxImageLength) 1326 1327 { 1328 1329 return false; 1330 1331 } 1332 1333 1334 1335 Byte[] bitary = null; 1336 1337 Int32 width = 0; 1338 1339 Int32 height = 0; 1340 1341 Boolean bl = GetBitArray(bitmap, ref bitary, ref width, ref height); 1342 1343 1344 1345 bl = PrintBitmapBytes(bitary, bitmap.Width, bitmap.Height); 1346 1347 return bl; 1348 1349 } 1350 1351 1352 1353 /// <summary> 1354 1355 /// 打印图片 1356 1357 /// </summary> 1358 1359 /// <param name="bitmap"></param> 1360 1361 /// <returns></returns> 1362 1363 public bool PrintBitmapBytes(Byte[] imgbitarray, Int32 width, Int32 height) 1364 1365 { 1366 1367 if (!m_Inited) 1368 1369 { 1370 1371 return false; 1372 1373 } 1374 1375 Int32 bytes = width * height / 8; 1376 1377 //检查是否尺寸符合要求 1378 1379 if (width > Const_MaxImageLength || height > Const_MaxFeedLength || 1380 1381 width < 1 || height < 1 || 1382 1383 imgbitarray == null) 1384 1385 { 1386 1387 return false; 1388 1389 } 1390 1391 1392 1393 //每次获取24行的数据进行发送,这24行的字节数 1394 1395 Int32 blockbytes = width * Const_OncePrintRowCount / 8; 1396 1397 if (blockbytes < 1) 1398 1399 { 1400 1401 return false; 1402 1403 } 1404 1405 1406 1407 Boolean bl = false; 1408 1409 1410 1411 //一共需要发送的块数量 1412 1413 Int32 blocks = imgbitarray.Length / blockbytes; 1414 1415 1416 1417 //每次发送的数据字节数 = 1B 2A 21 2字节长度 + 数据内容 1418 1419 Byte[] cmdbytes = new Byte[5 + blockbytes]; 1420 1421 //指令 1422 1423 Array.Copy(Const_SetImageCommand, cmdbytes, 3); 1424 1425 //数据长度,即 每行的点数 1426 1427 DataFormatProcessor.Int16ToBytes(width, ref cmdbytes, 3); 1428 1429 //数据内容 1430 1431 for (Int32 blockidx = 0; blockidx < blocks; ++blockidx) 1432 1433 { 1434 1435 Array.Copy(imgbitarray, blockidx * blockbytes, cmdbytes, 5, blockbytes); 1436 1437 //发送当前指令 1438 1439 bl = SendCommand(cmdbytes); 1440 1441 if (!bl) return false; 1442 1443 //休眠20毫秒 1444 1445 Thread.Sleep(20); 1446 1447 //发送 打印指令 1448 1449 bl = SendCommand(Const_Cmd_Print); 1450 1451 if (!bl) return false; 1452 1453 } 1454 1455 1456 1457 return bl; 1458 1459 } 1460 1461 1462 1463 /// <summary> 1464 1465 /// 走纸 1466 1467 /// </summary> 1468 1469 /// <param name="length"></param> 1470 1471 /// <returns></returns> 1472 1473 public bool Feed(int length) 1474 1475 { 1476 1477 if (!m_Inited) 1478 1479 { 1480 1481 return false; 1482 1483 } 1484 1485 if (length < 1) 1486 1487 length = 1; 1488 1489 if (length > Const_MaxFeedLength) 1490 1491 { 1492 1493 length = Const_MaxFeedLength; 1494 1495 } 1496 1497 string len = length.ToString("X2"); 1498 1499 len = Const_FeedForward + len; 1500 1501 bool bl = SendCommand(len); 1502 1503 return bl; 1504 1505 } 1506 1507 1508 1509 /// <summary> 1510 1511 /// 回退走纸 1512 1513 /// </summary> 1514 1515 /// <param name="length"></param> 1516 1517 /// <returns></returns> 1518 1519 public bool FeedBack(int length) 1520 1521 { 1522 1523 if (!m_Inited) 1524 1525 { 1526 1527 return false; 1528 1529 } 1530 1531 if (length < 1) 1532 1533 length = 1; 1534 1535 if (length > Const_MaxFeedLength) 1536 1537 { 1538 1539 length = Const_MaxFeedLength; 1540 1541 } 1542 1543 string len = length.ToString("X2"); 1544 1545 len = Const_FeedBack + len; 1546 1547 bool bl = SendCommand(len); 1548 1549 return bl; 1550 1551 } 1552 1553 1554 1555 /// <summary> 1556 1557 /// 设置字体大小.本函数不可与SetBigFont同时使用 1558 1559 /// </summary> 1560 1561 /// <param name="sizerate">大小倍率,取值范围 1 - 8</param> 1562 1563 /// <returns></returns> 1564 1565 public bool SetFontSize(Int32 sizerate) 1566 1567 { 1568 1569 if (!m_Inited) 1570 1571 { 1572 1573 return false; 1574 1575 } 1576 1577 1578 1579 if (sizerate < 1) 1580 1581 { 1582 1583 sizerate = 1; 1584 1585 } 1586 1587 1588 1589 if (sizerate > Const_MaxFontSize) 1590 1591 { 1592 1593 sizerate = Const_MaxFontSize; 1594 1595 } 1596 1597 sizerate--; 1598 1599 String sizecodestr = Const_SetFontSize + sizerate.ToString("X1") + sizerate.ToString("X1"); 1600 1601 bool bl = SendCommand(sizecodestr); 1602 1603 return bl; 1604 1605 } 1606 1607 1608 1609 #endregion 1610 1611 1612 1613 1614 1615 1616 1617 } 1618 1619 } 1620 1621 1622 1623 3.图像处理 ImageProcessor 1624 using System; 1625 1626 using System.Collections.Generic; 1627 1628 using System.Linq; 1629 1630 using System.Text; 1631 1632 using System.Drawing; 1633 1634 using LaisonTech.CommonBLL; 1635 1636 using System.Drawing.Imaging; 1637 1638 using System.IO; 1639 1640 using System.Drawing.Drawing2D; 1641 1642 using System.Windows.Forms; 1643 1644 using AForge.Imaging.Filters; 1645 1646 1647 1648 namespace LaisonTech.MediaLib 1649 1650 { 1651 1652 /// <summary> 1653 1654 /// 图片格式 1655 1656 /// </summary> 1657 1658 public enum ePictureFileFormat 1659 1660 { 1661 1662 Bmp = 0, 1663 1664 Gif = 1, 1665 1666 Icon = 2, 1667 1668 Jpeg = 3, 1669 1670 Png = 4, 1671 1672 } 1673 1674 1675 1676 /// <summary> 1677 1678 /// 转为灰度图像的方式 1679 1680 /// </summary> 1681 1682 public enum eGrayMode 1683 1684 { 1685 1686 /// <summary> 1687 1688 /// 算数平均 1689 1690 /// </summary> 1691 1692 ArithmeticAverage = 0, 1693 1694 /// <summary> 1695 1696 /// 加权平均 1697 1698 /// </summary> 1699 1700 WeightedAverage = 1, 1701 1702 } 1703 1704 1705 1706 /// <summary> 1707 1708 /// 比较2个图片的指定区域范围,像素的相同类型 1709 1710 /// </summary> 1711 1712 public enum eAreaDifferentType 1713 1714 { 1715 1716 /// <summary> 1717 1718 /// 所有像素都相同 1719 1720 /// </summary> 1721 1722 AllSame = 0, 1723 1724 /// <summary> 1725 1726 /// 所有像素都不同 1727 1728 /// </summary> 1729 1730 AllDifferent = 1, 1731 1732 /// <summary> 1733 1734 /// 部分相同部分不同 1735 1736 /// </summary> 1737 1738 Partial = 2, 1739 1740 } 1741 1742 1743 1744 /// <summary> 1745 1746 /// 图片文件处理 1747 1748 /// </summary> 1749 1750 public class ImageProcessor 1751 1752 { 1753 1754 #region 常量定义 1755 1756 1757 1758 public const Byte Const_BrightnessWhite = 255; 1759 1760 public const Byte Const_BrightnessBlack = 0; 1761 1762 1763 1764 1765 1766 /// <summary> 1767 1768 /// 比较结果的图片里,亮度相同部分的填充颜色 1769 1770 /// </summary> 1771 1772 public static Color Const_SameBrightnessColor = Color.Black; 1773 1774 /// <summary> 1775 1776 /// 比较结果的图片里,亮度相同部分的填充颜色 1777 1778 /// </summary> 1779 1780 public static Color Const_DifferentBrightnessColor = Color.White; 1781 1782 1783 1784 public const Byte Const_BlackBrightness = 0; 1785 1786 public const Byte Const_WhiteBrightness = 255; 1787 1788 public const Int32 Const_MaxBrightness = 255; 1789 1790 1791 1792 public const Int32 Const_MinBrightness = -255; 1793 1794 1795 1796 /// <summary> 1797 1798 /// 亮度的中间值 1799 1800 /// </summary> 1801 1802 public const Int32 Const_MiddleBrightness = 128; 1803 1804 #endregion 1805 1806 1807 1808 #region 屏幕截图,打印 1809 1810 1811 1812 /// <summary> 1813 1814 /// 获取屏幕分辨率 1815 1816 /// </summary> 1817 1818 /// <param name="width"></param> 1819 1820 /// <param name="height"></param> 1821 1822 public static void GetScreenSize(ref Int32 width, ref Int32 height) 1823 1824 { 1825 1826 height = Screen.PrimaryScreen.Bounds.Height; 1827 1828 width = Screen.PrimaryScreen.Bounds.Width; 1829 1830 } 1831 1832 1833 1834 /// <summary> 1835 1836 ///截图指定控件上显示的内容 1837 1838 /// </summary> 1839 1840 /// <param name="ctrl"></param> 1841 1842 /// <returns></returns> 1843 1844 public static Image CaptureControlImage(Control ctrl) 1845 1846 { 1847 1848 if (ctrl == null) 1849 1850 { 1851 1852 return null; 1853 1854 } 1855 1856 1857 1858 Control parent = ctrl; 1859 1860 if (ctrl.Parent != null) 1861 1862 { 1863 1864 parent = ctrl.Parent; 1865 1866 } 1867 1868 Point screenPoint = parent.PointToScreen(ctrl.Location); 1869 1870 1871 1872 Image ret = new Bitmap(ctrl.Width, ctrl.Height); 1873 1874 Graphics g = Graphics.FromImage(ret); 1875 1876 g.CopyFromScreen(screenPoint.X, screenPoint.Y, 1877 1878 0, 0, ctrl.Size); 1879 1880 g.DrawImage(ret, 0, 0); 1881 1882 1883 1884 return ret; 1885 1886 } 1887 1888 1889 1890 1891 1892 #endregion 1893 1894 1895 1896 #region 装载图片 1897 1898 1899 1900 /// <summary> 1901 1902 /// 装载图像文件 1903 1904 /// </summary> 1905 1906 /// <param name="filename"></param> 1907 1908 /// <returns></returns> 1909 1910 public static Image LoadImage(String filename) 1911 1912 { 1913 1914 //Boolean bl = FileProcessor.FileExist(filename); 1915 1916 //if (!bl) 1917 1918 //{ 1919 1920 // return null; 1921 1922 //} 1923 1924 //Bitmap image = (Bitmap)Bitmap.FromFile(filename); 1925 1926 //return image; 1927 1928 1929 1930 //以上方法会导致图片文件被锁定,无法删除移动 1931 1932 1933 1934 Byte[] photodata = null; 1935 1936 Boolean bl = FileProcessor.FileExist(filename); 1937 1938 if (!bl) 1939 1940 { 1941 1942 return null; 1943 1944 } 1945 1946 1947 1948 bl = FileProcessor.ReadFileBytes(filename, out photodata); 1949 1950 if (!bl) 1951 1952 { 1953 1954 return null; 1955 1956 } 1957 1958 1959 1960 MemoryStream ms = null; 1961 1962 Image myImage = null; 1963 1964 try 1965 1966 { 1967 1968 ms = new MemoryStream(photodata); 1969 1970 myImage = Bitmap.FromStream(ms); 1971 1972 ms.Close(); 1973 1974 } 1975 1976 catch (System.Exception ex) 1977 1978 { 1979 1980 Console.WriteLine("LoadImage error:" + ex.Message); 1981 1982 myImage = null; 1983 1984 } 1985 1986 return myImage; 1987 1988 } 1989 1990 1991 1992 /// <summary> 1993 1994 /// 装载图像文件 1995 1996 /// </summary> 1997 1998 /// <param name="filename"></param> 1999 2000 /// <returns></returns> 2001 2002 public static Bitmap LoadBitImage(String filename) 2003 2004 { 2005 2006 Bitmap ret = (Bitmap)LoadImage(filename); 2007 2008 return ret; 2009 2010 } 2011 2012 2013 2014 /// <summary> 2015 2016 /// 保存图片到指定路径 2017 2018 /// </summary> 2019 2020 /// <param name="img"></param> 2021 2022 /// <param name="filename"></param> 2023 2024 /// <returns></returns> 2025 2026 public static Boolean SaveImage(Image img, String filename) 2027 2028 { 2029 2030 FileProcessor.DeleteFile(filename); 2031 2032 if (img == null) 2033 2034 { 2035 2036 return false; 2037 2038 } 2039 2040 //获取保存图片的路径,如果路径不存在,新建 2041 2042 String folder = FileProcessor.GetDirectoryName(filename); 2043 2044 if (!FileProcessor.DirectoryExist(folder)) 2045 2046 { 2047 2048 FileProcessor.CreateDirectory(folder); 2049 2050 } 2051 2052 img.Save(filename); 2053 2054 Boolean bl = FileProcessor.FileExist(filename); 2055 2056 return bl; 2057 2058 } 2059 2060 2061 2062 #endregion 2063 2064 2065 2066 #region 转换图片格式 2067 2068 2069 2070 /// <summary> 2071 2072 /// 转换图片格式 2073 2074 /// </summary> 2075 2076 /// <param name="bmpfilename"></param> 2077 2078 /// <param name="jpgfilename"></param> 2079 2080 /// <returns></returns> 2081 2082 public static Boolean BmpToJpg(String bmpfilename, String jpgfilename) 2083 2084 { 2085 2086 Boolean bl = ChangeFileFormat(bmpfilename, jpgfilename, ePictureFileFormat.Jpeg); 2087 2088 return bl; 2089 2090 } 2091 2092 2093 2094 /// <summary> 2095 2096 /// 转换图片格式 2097 2098 /// </summary> 2099 2100 /// <param name="srcfilename"></param> 2101 2102 /// <param name="destfilename"></param> 2103 2104 /// <param name="destformat"></param> 2105 2106 /// <returns></returns> 2107 2108 public static Boolean ChangeFileFormat(String srcfilename, String destfilename, ePictureFileFormat destformat) 2109 2110 { 2111 2112 Boolean bl = FileProcessor.FileExist(srcfilename); 2113 2114 if (!bl) 2115 2116 { 2117 2118 return false; 2119 2120 } 2121 2122 Image image = Image.FromFile(srcfilename); 2123 2124 2125 2126 ImageFormat IFMT = null; 2127 2128 switch (destformat) 2129 2130 { 2131 2132 case ePictureFileFormat.Bmp: 2133 2134 IFMT = ImageFormat.Bmp; 2135 2136 break; 2137 2138 case ePictureFileFormat.Gif: 2139 2140 IFMT = ImageFormat.Gif; 2141 2142 break; 2143 2144 case ePictureFileFormat.Icon: 2145 2146 IFMT = ImageFormat.Icon; 2147 2148 break; 2149 2150 case ePictureFileFormat.Jpeg: 2151 2152 IFMT = ImageFormat.Jpeg; 2153 2154 break; 2155 2156 case ePictureFileFormat.Png: 2157 2158 IFMT = ImageFormat.Png; 2159 2160 break; 2161 2162 default: 2163 2164 IFMT = ImageFormat.Jpeg; 2165 2166 break; 2167 2168 } 2169 2170 image.Save(destfilename, IFMT); 2171 2172 image.Dispose(); 2173 2174 2175 2176 bl = FileProcessor.FileExist(destfilename); 2177 2178 if (!bl) 2179 2180 { 2181 2182 return false; 2183 2184 } 2185 2186 2187 2188 Int32 filelen = FileProcessor.GetFileLength(destfilename); 2189 2190 return (filelen > 0); 2191 2192 } 2193 2194 2195 2196 /// <summary> 2197 2198 /// 变成黑白图 2199 2200 /// </summary> 2201 2202 /// <param name="srcbitmap">原始图</param> 2203 2204 /// <param name="mode">模式。0:加权平均 1:算数平均</param> 2205 2206 /// <returns></returns> 2207 2208 public static Bitmap ToGray(Bitmap bitmap, eGrayMode mode = eGrayMode.ArithmeticAverage) 2209 2210 { 2211 2212 if (bitmap == null) 2213 2214 { 2215 2216 return null; 2217 2218 } 2219 2220 2221 2222 int width = bitmap.Width; 2223 2224 int height = bitmap.Height; 2225 2226 byte newColor = 0; 2227 2228 try 2229 2230 { 2231 2232 BitmapData srcData = bitmap.LockBits(new Rectangle(0, 0, width, height), 2233 2234 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 2235 2236 unsafe 2237 2238 { 2239 2240 byte* curpix = (byte*)srcData.Scan0.ToPointer(); 2241 2242 if (mode == eGrayMode.ArithmeticAverage)// 算数平均 2243 2244 { 2245 2246 for (int y = 0; y < height; y++) 2247 2248 { 2249 2250 for (int x = 0; x < width; x++) 2251 2252 { 2253 2254 newColor = (byte)((float)(curpix[0] + curpix[1] + curpix[2]) / 3.0f); 2255 2256 curpix[0] = newColor; 2257 2258 curpix[1] = newColor; 2259 2260 curpix[2] = newColor; 2261 2262 curpix += 3; 2263 2264 } 2265 2266 curpix += srcData.Stride - width * 3; 2267 2268 } 2269 2270 } 2271 2272 else 2273 2274 { 2275 2276 // 加权平均 2277 2278 for (int y = 0; y < height; y++) 2279 2280 { 2281 2282 for (int x = 0; x < width; x++) 2283 2284 { 2285 2286 newColor = (byte)((float)curpix[0] * 0.114f + (float)curpix[1] * 0.587f + (float)curpix[2] * 0.299f); 2287 2288 curpix[0] = newColor; 2289 2290 curpix[1] = newColor; 2291 2292 curpix[2] = newColor; 2293 2294 curpix += 3; 2295 2296 } 2297 2298 curpix += srcData.Stride - width * 3; 2299 2300 } 2301 2302 } 2303 2304 bitmap.UnlockBits(srcData); 2305 2306 } 2307 2308 } 2309 2310 catch 2311 2312 { 2313 2314 bitmap = null; 2315 2316 } 2317 2318 2319 2320 return bitmap; 2321 2322 } 2323 2324 2325 2326 /// <summary> 2327 2328 /// 获取一幅图片对应的所有像素亮度数组 2329 2330 /// </summary> 2331 2332 /// <param name="bitmap">原始图</param> 2333 2334 /// <param name="brightnessary">亮度值数组</param> 2335 2336 /// <param name="mode">模式。0:加权平均 1:算数平均</param> 2337 2338 /// <returns></returns> 2339 2340 public static Boolean GetImageBrightness(Bitmap bitmap, ref Byte[] brightnessary, 2341 2342 eGrayMode mode = eGrayMode.WeightedAverage) 2343 2344 { 2345 2346 if (bitmap == null) 2347 2348 { 2349 2350 return false; 2351 2352 } 2353 2354 2355 2356 int width = bitmap.Width; 2357 2358 int height = bitmap.Height; 2359 2360 if (width < 1 || height < 1) 2361 2362 { 2363 2364 return false; 2365 2366 } 2367 2368 2369 2370 brightnessary = new Byte[width * height]; 2371 2372 Boolean bl = false; 2373 2374 Int32 rowredundancy = 0;//每一行像素,对应的数组长度 与 实际像素点数的差值 2375 2376 Int32 pixidx = 0;//像素下标 2377 2378 try 2379 2380 { 2381 2382 BitmapData srcData = bitmap.LockBits(new Rectangle(0, 0, width, height), 2383 2384 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 2385 2386 rowredundancy = srcData.Stride - width * 3;//每行末尾还有这么多的冗余字节 2387 2388 2389 2390 unsafe 2391 2392 { 2393 2394 byte* curpix = (byte*)srcData.Scan0.ToPointer(); 2395 2396 if (mode == eGrayMode.ArithmeticAverage)// 算数平均 2397 2398 { 2399 2400 for (int y = 0; y < height; y++) 2401 2402 { 2403 2404 for (int x = 0; x < width; x++) 2405 2406 { 2407 2408 brightnessary[pixidx] = (byte)((float)(curpix[0] + curpix[1] + curpix[2]) / 3.0f); 2409 2410 ++pixidx; 2411 2412 curpix += 3; 2413 2414 } 2415 2416 curpix += rowredundancy; 2417 2418 } 2419 2420 } 2421 2422 else 2423 2424 { 2425 2426 // 加权平均 2427 2428 for (int y = 0; y < height; y++) 2429 2430 { 2431 2432 for (int x = 0; x < width; x++) 2433 2434 { 2435 2436 brightnessary[pixidx] = (byte)((float)curpix[0] * 0.114f + (float)curpix[1] * 0.587f + (float)curpix[2] * 0.299f); 2437 2438 ++pixidx; 2439 2440 curpix += 3; 2441 2442 } 2443 2444 curpix += rowredundancy; 2445 2446 } 2447 2448 } 2449 2450 bitmap.UnlockBits(srcData); 2451 2452 } 2453 2454 bl = true; 2455 2456 } 2457 2458 catch(Exception ex) 2459 2460 { 2461 2462 bl = false; 2463 2464 Console.WriteLine("Get brightness ary error:" + ex.Message); 2465 2466 } 2467 2468 2469 2470 return bl; 2471 2472 } 2473 2474 2475 2476 /// <summary> 2477 2478 /// 变成黑白图,每个元素都是一个像素的亮度 2479 2480 /// </summary> 2481 2482 /// <param name=" bitmap ">原始图</param> 2483 2484 /// <param name=" graybitmap ">黑白图</param> 2485 2486 /// <param name=" brightnessbytes ">黑白所有像素点亮度</param> 2487 2488 /// <param name="mode">模式。0:加权平均 1:算数平均</param> 2489 2490 /// <returns></returns> 2491 2492 public static Boolean ToGray(Bitmap bitmap, ref Bitmap graybitmap, ref Byte[] brightnessbytes, 2493 2494 eGrayMode mode = eGrayMode.WeightedAverage) 2495 2496 { 2497 2498 if (bitmap == null) 2499 2500 { 2501 2502 return false; 2503 2504 } 2505 2506 2507 2508 brightnessbytes = new Byte[bitmap.Width * bitmap.Height]; 2509 2510 2511 2512 int width = bitmap.Width; 2513 2514 int height = bitmap.Height; 2515 2516 //Clone 可能引发 GDI+异常 2517 2518 graybitmap = new Bitmap(bitmap); 2519 2520 2521 2522 byte newColor = 0; 2523 2524 Int32 bytesidx = 0; 2525 2526 Boolean bl = false; 2527 2528 try 2529 2530 { 2531 2532 BitmapData srcData = graybitmap.LockBits(new Rectangle(0, 0, width, height), 2533 2534 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 2535 2536 unsafe 2537 2538 { 2539 2540 byte* curpix = (byte*)srcData.Scan0.ToPointer(); 2541 2542 if (mode == eGrayMode.ArithmeticAverage)// 算数平均 2543 2544 { 2545 2546 for (int y = 0; y < height; y++) 2547 2548 { 2549 2550 for (int x = 0; x < width; x++) 2551 2552 { 2553 2554 newColor = (byte)((float)(curpix[0] + curpix[1] + curpix[2]) / 3.0f); 2555 2556 2557 2558 brightnessbytes[bytesidx] = newColor; 2559 2560 ++bytesidx; 2561 2562 2563 2564 curpix[0] = newColor; 2565 2566 curpix[1] = newColor; 2567 2568 curpix[2] = newColor; 2569 2570 curpix += 3; 2571 2572 } 2573 2574 curpix += srcData.Stride - width * 3; 2575 2576 } 2577 2578 } 2579 2580 else 2581 2582 { 2583 2584 // 加权平均 2585 2586 for (int y = 0; y < height; y++) 2587 2588 { 2589 2590 for (int x = 0; x < width; x++) 2591 2592 { 2593 2594 newColor = (byte)((float)curpix[0] * 0.114f + (float)curpix[1] * 0.587f + (float)curpix[2] * 0.299f); 2595 2596 2597 2598 brightnessbytes[bytesidx] = newColor; 2599 2600 ++bytesidx; 2601 2602 2603 2604 curpix[0] = newColor; 2605 2606 curpix[1] = newColor; 2607 2608 curpix[2] = newColor; 2609 2610 curpix += 3; 2611 2612 } 2613 2614 curpix += srcData.Stride - width * 3; 2615 2616 } 2617 2618 } 2619 2620 graybitmap.UnlockBits(srcData); 2621 2622 } 2623 2624 2625 2626 bl = true; 2627 2628 } 2629 2630 catch(Exception ex) 2631 2632 { 2633 2634 graybitmap = null; 2635 2636 Console.WriteLine("ToGray error:" + ex.Message); 2637 2638 bl = false; 2639 2640 } 2641 2642 2643 2644 return bl; 2645 2646 } 2647 2648 2649 2650 2651 2652 /// <summary> 2653 2654 /// 把图片转换为非黑即白的二色图. 2655 2656 /// </summary> 2657 2658 /// <param name="bitmap">原始图</param> 2659 2660 /// <param name="brightnessGate">亮度门限.超过此亮度认为白点,否则认为黑点</param> 2661 2662 /// <param name="bitary">每个像素点是否为黑点的数组</param> 2663 2664 /// <param name="trueAsblack">true-每个元素黑点为true,白点为false; false-每个元素白点为true,黑点为false</param> 2665 2666 /// <returns></returns> 2667 2668 public static Boolean ToBooleanArray(Bitmap bitmap, Byte brightnessGate, ref Boolean[] bitary, Boolean trueAsblack = true) 2669 2670 { 2671 2672 if (bitmap == null) 2673 2674 { 2675 2676 return false; 2677 2678 } 2679 2680 2681 2682 bitary = new Boolean[bitmap.Width * bitmap.Height]; 2683 2684 2685 2686 int width = bitmap.Width; 2687 2688 int height = bitmap.Height; 2689 2690 2691 2692 byte curcolor = 0; 2693 2694 Int32 pixidx = 0; 2695 2696 Boolean bl = false; 2697 2698 try 2699 2700 { 2701 2702 BitmapData srcData = bitmap.LockBits(new Rectangle(0, 0, width, height), 2703 2704 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 2705 2706 unsafe 2707 2708 { 2709 2710 byte* curpix = (byte*)srcData.Scan0.ToPointer(); 2711 2712 2713 2714 for (int y = 0; y < height; y++) 2715 2716 { 2717 2718 for (int x = 0; x < width; x++) 2719 2720 { 2721 2722 curcolor = (byte)((float)(curpix[0] + curpix[1] + curpix[2]) / 3.0f); 2723 2724 2725 2726 if (trueAsblack)//true为黑点 2727 2728 { 2729 2730 bitary[pixidx] = (curcolor < brightnessGate); 2731 2732 } 2733 2734 else 2735 2736 { 2737 2738 //true为白点 2739 2740 bitary[pixidx] = (curcolor > brightnessGate); 2741 2742 } 2743 2744 ++pixidx; 2745 2746 curpix += 3; 2747 2748 } 2749 2750 curpix += srcData.Stride - width * 3; 2751 2752 } 2753 2754 bitmap.UnlockBits(srcData); 2755 2756 } 2757 2758 2759 2760 bl = true; 2761 2762 } 2763 2764 catch (Exception ex) 2765 2766 { 2767 2768 Console.WriteLine("ToGray error:" + ex.Message); 2769 2770 bl = false; 2771 2772 } 2773 2774 2775 2776 return bl; 2777 2778 } 2779 2780 2781 2782 /// <summary> 2783 2784 /// 亮度差数组变成bool数组.true表示亮度不同,false表示亮度相同 2785 2786 /// </summary> 2787 2788 /// <param name="bridiffary">亮度差数组</param> 2789 2790 /// <param name="brightnessGate">亮度门限.超过此亮度认为白点,否则认为黑点</param> 2791 2792 /// <returns></returns> 2793 2794 public static Boolean BrightnessToBoolean(Byte[] bridiffary, Byte brightnessGate, ref Boolean[] boolary) 2795 2796 { 2797 2798 if (bridiffary == null || bridiffary.Length < 4) 2799 2800 { 2801 2802 return false; 2803 2804 } 2805 2806 2807 2808 boolary = new Boolean[bridiffary.Length]; 2809 2810 2811 2812 for (Int32 idx = 0; idx < bridiffary.Length; ++idx) 2813 2814 { 2815 2816 boolary[idx] = (bridiffary[idx] > brightnessGate); 2817 2818 } 2819 2820 2821 2822 return true; 2823 2824 } 2825 2826 2827 2828 #endregion 2829 2830 2831 2832 #region 图片调整 2833 2834 2835 2836 /// <summary> 2837 2838 /// 调整亮度 2839 2840 /// </summary> 2841 2842 /// <param name="bitmap">原始图</param> 2843 2844 /// <param name="degree">亮度,取值范围-255 - 255</param> 2845 2846 /// <returns></returns> 2847 2848 public static Bitmap SetBrightness(Bitmap srcbitmap, int brightnessOffset) 2849 2850 { 2851 2852 if (srcbitmap == null) 2853 2854 { 2855 2856 return null; 2857 2858 } 2859 2860 2861 2862 CommonCompute.SetInt32Range(ref brightnessOffset, Const_MinBrightness, Const_MaxBrightness); 2863 2864 int width = srcbitmap.Width; 2865 2866 int height = srcbitmap.Height; 2867 2868 2869 2870 Bitmap bitmap = (Bitmap)srcbitmap.Clone(); 2871 2872 2873 2874 try 2875 2876 { 2877 2878 BitmapData data = bitmap.LockBits(new Rectangle(0, 0, width, height), 2879 2880 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 2881 2882 2883 2884 unsafe 2885 2886 { 2887 2888 byte* curpix = (byte*)data.Scan0.ToPointer(); 2889 2890 Int32 curcolor = 0; 2891 2892 for (int y = 0; y < height; y++) 2893 2894 { 2895 2896 for (int x = 0; x < width; x++) 2897 2898 { 2899 2900 curcolor = curpix[0] + brightnessOffset; 2901 2902 CommonCompute.SetInt32Range(ref curcolor, 0, Const_MaxBrightness); 2903 2904 curpix[0] = (byte)curcolor; 2905 2906 2907 2908 curcolor = curpix[1] + brightnessOffset; 2909 2910 CommonCompute.SetInt32Range(ref curcolor, 0, Const_MaxBrightness); 2911 2912 curpix[1] = (byte)curcolor; 2913 2914 2915 2916 curcolor = curpix[2] + brightnessOffset; 2917 2918 CommonCompute.SetInt32Range(ref curcolor, 0, Const_MaxBrightness); 2919 2920 curpix[2] = (byte)curcolor; 2921 2922 2923 2924 curpix += 3; 2925 2926 } 2927 2928 curpix += data.Stride - width * 3; 2929 2930 } 2931 2932 } 2933 2934 2935 2936 bitmap.UnlockBits(data); 2937 2938 } 2939 2940 catch 2941 2942 { 2943 2944 bitmap = null; 2945 2946 } 2947 2948 2949 2950 return bitmap; 2951 2952 } 2953 2954 2955 2956 /// <summary> 2957 2958 /// 调整图像对比度 2959 2960 /// </summary> 2961 2962 /// <param name="bitmap">原始图</param> 2963 2964 /// <param name="degree">对比度 0 - 100</param> 2965 2966 /// <returns></returns> 2967 2968 public static Bitmap SetContrast(Bitmap srcbitmap, int contrast) 2969 2970 { 2971 2972 if (srcbitmap == null) 2973 2974 { 2975 2976 return null; 2977 2978 } 2979 2980 2981 2982 //对比度取值范围,0-100 2983 2984 CommonCompute.SetInt32Range(ref contrast, 0, 100); 2985 2986 2987 2988 Int32 curcolor = 0; 2989 2990 Bitmap bitmap = (Bitmap)srcbitmap.Clone(); 2991 2992 int width = bitmap.Width; 2993 2994 int height = bitmap.Height; 2995 2996 2997 2998 //调整对比度基本思路:0时,所有像素点的亮度都设置为中间值128 2999 3000 //100 时,把亮度大于128的像素,亮度设置为255;小于128的设置为0 3001 3002 //即:50时,保持不变;小于50,所有点的亮度向中间值128偏移;大于50,所有点亮度值向两端偏移 3003 3004 3005 3006 //如果当前像素点的亮度是130, 对比度为50时,结果仍然要是130,此时rate为1.0 3007 3008 //对比度为100时,结果要变成255,此时rate >= 128 3009 3010 //对比度为0时,结果要变成128,此时rate = 0 3011 3012 //因此可知对比度与rate的对应关系 3013 3014 //对比度: 0 50 100 3015 3016 //rate : 0 1 127 3017 3018 double rate = 0; 3019 3020 if (contrast == 50) 3021 3022 { 3023 3024 rate = 1; 3025 3026 } 3027 3028 else if (contrast < 50) 3029 3030 { 3031 3032 rate = contrast / 50.0;//小于50的,对比度比率必须是纯小数,0-1 之间 3033 3034 } 3035 3036 else 3037 3038 { 3039 3040 rate = 1 + Const_MiddleBrightness * ((contrast - 50.0) / 50.0);//大于50的,比率必须是1到128之间的值 3041 3042 } 3043 3044 3045 3046 try 3047 3048 { 3049 3050 3051 3052 BitmapData data = bitmap.LockBits(new Rectangle(0, 0, width, height), 3053 3054 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 3055 3056 unsafe 3057 3058 { 3059 3060 byte* curpix = (byte*)data.Scan0.ToPointer(); 3061 3062 3063 3064 for (int y = 0; y < height; y++) 3065 3066 { 3067 3068 for (int x = 0; x < width; x++) 3069 3070 { 3071 3072 for (int i = 0; i < 3; i++) //R,G,B 3个通道 3073 3074 { 3075 3076 //对于 刚好亮度等于中间值的点,需要把亮度调高或调低1 3077 3078 //否则将无法实现对该点 提高对比度 3079 3080 if (curpix[i] == Const_MiddleBrightness) 3081 3082 { 3083 3084 curpix[i] = (byte)(curpix[i] + 1); 3085 3086 } 3087 3088 3089 3090 //调整该像素对比度 3091 3092 curcolor = (Int32)(Const_MiddleBrightness + (curpix[i] - Const_MiddleBrightness) * rate); 3093 3094 CommonCompute.SetInt32Range(ref curcolor, Const_MinBrightness, Const_MaxBrightness); 3095 3096 curpix[i] = (byte)curcolor; 3097 3098 ++curpix; 3099 3100 } 3101 3102 } 3103 3104 3105 3106 curpix += data.Stride - width * 3; 3107 3108 } 3109 3110 } 3111 3112 bitmap.UnlockBits(data); 3113 3114 } 3115 3116 catch 3117 3118 { 3119 3120 bitmap = null; 3121 3122 } 3123 3124 3125 3126 return bitmap; 3127 3128 } 3129 3130 3131 3132 /// <summary> 3133 3134 /// 任意角度旋转 3135 3136 /// </summary> 3137 3138 /// <param name="srcbitmap">原始图Bitmap</param> 3139 3140 /// <param name="angle">旋转角度</param> 3141 3142 /// <param name="bkColor">背景色</param> 3143 3144 /// <returns>输出Bitmap</returns> 3145 3146 public static Bitmap Rotate(Bitmap srcbitmap, float angle, Color bkColor) 3147 3148 { 3149 3150 int w = srcbitmap.Width + 2; 3151 3152 int h = srcbitmap.Height + 2; 3153 3154 3155 3156 PixelFormat pf; 3157 3158 3159 3160 if (bkColor == Color.Transparent) 3161 3162 { 3163 3164 pf = PixelFormat.Format32bppArgb; 3165 3166 } 3167 3168 else 3169 3170 { 3171 3172 pf = srcbitmap.PixelFormat; 3173 3174 } 3175 3176 3177 3178 Bitmap tmp = new Bitmap(w, h, pf); 3179 3180 Graphics g = Graphics.FromImage(tmp); 3181 3182 g.Clear(bkColor); 3183 3184 g.DrawImageUnscaled(srcbitmap, 1, 1); 3185 3186 g.Dispose(); 3187 3188 3189 3190 GraphicsPath path = new GraphicsPath(); 3191 3192 path.AddRectangle(new RectangleF(0f, 0f, w, h)); 3193 3194 Matrix mtrx = new Matrix(); 3195 3196 mtrx.Rotate(angle); 3197 3198 RectangleF rct = path.GetBounds(mtrx); 3199 3200 3201 3202 Bitmap dst = new Bitmap((int)rct.Width, (int)rct.Height, pf); 3203 3204 g = Graphics.FromImage(dst); 3205 3206 g.Clear(bkColor); 3207 3208 g.TranslateTransform(-rct.X, -rct.Y); 3209 3210 g.RotateTransform(angle); 3211 3212 g.InterpolationMode = InterpolationMode.HighQualityBilinear; 3213 3214 g.DrawImageUnscaled(tmp, 0, 0); 3215 3216 g.Dispose(); 3217 3218 3219 3220 tmp.Dispose(); 3221 3222 3223 3224 return dst; 3225 3226 } 3227 3228 3229 3230 /// <summary> 3231 3232 /// Gamma校正 3233 3234 /// </summary> 3235 3236 /// <param name="srcbitmap">输入Bitmap</param> 3237 3238 /// <param name="val">[0 <-明- 1 -暗-> 2]</param> 3239 3240 /// <returns>输出Bitmap</returns> 3241 3242 public static Bitmap SetGamma(Bitmap srcbitmap, float val) 3243 3244 { 3245 3246 if (srcbitmap == null) 3247 3248 { 3249 3250 return null; 3251 3252 } 3253 3254 3255 3256 // 1表示无变化,就不做 3257 3258 if (val == 1.0000f) return srcbitmap; 3259 3260 3261 3262 try 3263 3264 { 3265 3266 Bitmap b = new Bitmap(srcbitmap.Width, srcbitmap.Height); 3267 3268 Graphics g = Graphics.FromImage(b); 3269 3270 ImageAttributes attr = new ImageAttributes(); 3271 3272 3273 3274 attr.SetGamma(val, ColorAdjustType.Bitmap); 3275 3276 g.DrawImage(srcbitmap, new Rectangle(0, 0, srcbitmap.Width, srcbitmap.Height), 0, 0, srcbitmap.Width, srcbitmap.Height, GraphicsUnit.Pixel, attr); 3277 3278 g.Dispose(); 3279 3280 return b; 3281 3282 } 3283 3284 catch 3285 3286 { 3287 3288 return null; 3289 3290 } 3291 3292 } 3293 3294 3295 3296 /// <summary> 3297 3298 /// 重新设置图片尺寸 3299 3300 /// </summary> 3301 3302 /// <param name="srcbitmap">original Bitmap</param> 3303 3304 /// <param name="newW">new width</param> 3305 3306 /// <param name="newH">new height</param> 3307 3308 /// <returns>worked bitmap</returns> 3309 3310 public static Boolean ResizeImage(Bitmap srcimg, int newW, int newH, ref Bitmap destimage) 3311 3312 { 3313 3314 if (srcimg == null) 3315 3316 { 3317 3318 return false; 3319 3320 } 3321 3322 3323 3324 destimage = new Bitmap(newW, newH); 3325 3326 Graphics graph = Graphics.FromImage(destimage); 3327 3328 Boolean bl = true; 3329 3330 try 3331 3332 { 3333 3334 graph.InterpolationMode = InterpolationMode.HighQualityBicubic; 3335 3336 graph.DrawImage(srcimg, new Rectangle(0, 0, newW, newH), 3337 3338 new Rectangle(0, 0, srcimg.Width, srcimg.Height), 3339 3340 GraphicsUnit.Pixel); 3341 3342 3343 3344 graph.Dispose(); 3345 3346 } 3347 3348 catch (Exception ex) 3349 3350 { 3351 3352 Console.WriteLine("ResizeImage error" + ex.Message); 3353 3354 bl = false; 3355 3356 } 3357 3358 return bl; 3359 3360 } 3361 3362 3363 3364 3365 3366 /// <summary> 3367 3368 /// 去除噪点 3369 3370 /// </summary> 3371 3372 /// <param name="noisypointsize">噪点的尺寸</param> 3373 3374 /// <param name="bitmap">待处理的图片信息</param> 3375 3376 /// <returns></returns> 3377 3378 public static Boolean RemoveNoisypoint(Int32 noisypointsize, ref Bitmap bitmap) 3379 3380 { 3381 3382 if (bitmap == null || noisypointsize < 1 || 3383 3384 noisypointsize * 2 >= bitmap.Width || noisypointsize * 2 >= bitmap.Height) 3385 3386 { 3387 3388 return false; 3389 3390 } 3391 3392 3393 3394 // 创建过滤器 3395 3396 BlobsFiltering blobfilter = 3397 3398 new BlobsFiltering(); 3399 3400 // 设置过滤条件(对象长、宽至少为70) 3401 3402 blobfilter.CoupledSizeFiltering = true; 3403 3404 blobfilter.MinWidth = noisypointsize; 3405 3406 blobfilter.MinHeight = noisypointsize; 3407 3408 blobfilter.ApplyInPlace(bitmap); 3409 3410 3411 3412 return true; 3413 3414 } 3415 3416 3417 3418 /// <summary> 3419 3420 /// 把图片里指定区域的内容复制到另一个图片里 3421 3422 /// </summary> 3423 3424 /// <param name="srcimg"></param> 3425 3426 /// <param name="x"></param> 3427 3428 /// <param name="y"></param> 3429 3430 /// <param name="width"></param> 3431 3432 /// <param name="height"></param> 3433 3434 /// <param name="destimg"></param> 3435 3436 /// <returns></returns> 3437 3438 public static Boolean CutImage(Bitmap srcimg, Int32 x, Int32 y, Int32 width, Int32 height, ref Bitmap destimg) 3439 3440 { 3441 3442 if (srcimg == null || x < 0 || y < 0 || width < 1 || height < 1 || 3443 3444 x + width > srcimg.Width || y + height > srcimg.Height) 3445 3446 { 3447 3448 return false; 3449 3450 } 3451 3452 3453 3454 destimg = new Bitmap(width, height, PixelFormat.Format32bppArgb); 3455 3456 Graphics graph = Graphics.FromImage(destimg); 3457 3458 graph.InterpolationMode = InterpolationMode.HighQualityBicubic; 3459 3460 graph.DrawImage(srcimg, new Rectangle(0, 0, width, height), 3461 3462 new Rectangle(x, y, width, height), GraphicsUnit.Pixel); 3463 3464 graph.Dispose(); 3465 3466 return true; 3467 3468 } 3469 3470 3471 3472 #endregion 3473 3474 3475 3476 #region 亮度处理 3477 3478 3479 3480 3481 3482 3483 3484 /// <summary> 3485 3486 /// 获取指定坐标处的亮度值 3487 3488 /// </summary> 3489 3490 /// <param name="bitmap"></param> 3491 3492 /// <param name="x"></param> 3493 3494 /// <param name="y"></param> 3495 3496 /// <returns></returns> 3497 3498 public static Boolean GetPixBrightness(Bitmap bitmap, Int32 x, Int32 y, eGrayMode mode, ref Byte brightness) 3499 3500 { 3501 3502 if (bitmap == null) 3503 3504 { 3505 3506 return false; 3507 3508 } 3509 3510 3511 3512 if (x < 0 || x >= bitmap.Width || 3513 3514 y < 0 || y >= bitmap.Height) 3515 3516 { 3517 3518 return false; 3519 3520 } 3521 3522 3523 3524 Color curColor = bitmap.GetPixel(x, y); 3525 3526 //利用公式计算灰度值(加权平均法) 3527 3528 if (mode == eGrayMode.ArithmeticAverage) 3529 3530 { 3531 3532 brightness = (Byte)(curColor.R * 0.299f + curColor.G * 0.587f + curColor.B * 0.114f); 3533 3534 } 3535 3536 else 3537 3538 { 3539 3540 brightness = (Byte)((curColor.R + curColor.G + curColor.B) / 3.0f); 3541 3542 } 3543 3544 return true; 3545 3546 } 3547 3548 3549 3550 /// <summary> 3551 3552 /// 获取指定坐标处的亮度值 3553 3554 /// </summary> 3555 3556 /// <param name="bitmap"></param> 3557 3558 /// <param name="x"></param> 3559 3560 /// <param name="y"></param> 3561 3562 /// <returns></returns> 3563 3564 public static Boolean GetPixBrightness(Byte[] bribytes, Int32 width,Int32 height, 3565 3566 Int32 x, Int32 y, ref Byte brightness) 3567 3568 { 3569 3570 if (bribytes == null || width < 1 || height < 1 || 3571 3572 x < 0 || x >= width || 3573 3574 y < 0 || y >= height || 3575 3576 bribytes.Length != width * height) 3577 3578 { 3579 3580 return false; 3581 3582 } 3583 3584 3585 3586 brightness = bribytes[y * width + x]; 3587 3588 3589 3590 return true; 3591 3592 } 3593 3594 3595 3596 /// <summary> 3597 3598 /// 获取指定坐标处的亮度值 3599 3600 /// </summary> 3601 3602 /// <param name="bitmap"></param> 3603 3604 /// <param name="x"></param> 3605 3606 /// <param name="y"></param> 3607 3608 /// <returns></returns> 3609 3610 public static Boolean GetPixBrightnessByRate(Byte[] bribytes, Int32 width, Int32 height, 3611 3612 double xRate, double yRate, ref Byte brightness) 3613 3614 { 3615 3616 int x = (int)(width * xRate); 3617 3618 int y = (int)(height * yRate); 3619 3620 3621 3622 if (bribytes == null || width < 1 || height < 1 || 3623 3624 x < 0 || x >= width || 3625 3626 y < 0 || y >= height || 3627 3628 bribytes.Length != width * height) 3629 3630 { 3631 3632 return false; 3633 3634 } 3635 3636 3637 3638 brightness = bribytes[y * width + x]; 3639 3640 return true; 3641 3642 } 3643 3644 3645 3646 /// <summary> 3647 3648 /// 获取指定坐标处的颜色 3649 3650 /// </summary> 3651 3652 /// <param name="bitmap"></param> 3653 3654 /// <param name="x"></param> 3655 3656 /// <param name="y"></param> 3657 3658 /// <returns></returns> 3659 3660 public static Boolean GetPixColor(Bitmap bitmap, Int32 x, Int32 y, ref Color curColor) 3661 3662 { 3663 3664 if (bitmap == null) 3665 3666 { 3667 3668 return false; 3669 3670 } 3671 3672 3673 3674 if (x < 0 || x >= bitmap.Width || 3675 3676 y < 0 || y >= bitmap.Height) 3677 3678 { 3679 3680 return false; 3681 3682 } 3683 3684 3685 3686 curColor = bitmap.GetPixel(x, y); 3687 3688 return true; 3689 3690 } 3691 3692 3693 3694 /// <summary> 3695 3696 /// 获取指定坐标处的颜色 3697 3698 /// </summary> 3699 3700 /// <param name="bitmap"></param> 3701 3702 /// <param name="x"></param> 3703 3704 /// <param name="y"></param> 3705 3706 /// <returns></returns> 3707 3708 public static Boolean GetPixColorByRate(Bitmap bitmap, double xRate, double yRate, ref Color curColor) 3709 3710 { 3711 3712 if (bitmap == null) 3713 3714 { 3715 3716 return false; 3717 3718 } 3719 3720 3721 3722 int width = bitmap.Width; 3723 3724 int height = bitmap.Height; 3725 3726 int X = (int)(width * xRate); 3727 3728 int Y = (int)(height * yRate); 3729 3730 3731 3732 Boolean bl = GetPixColor(bitmap, X, Y, ref curColor); 3733 3734 return bl; 3735 3736 } 3737 3738 3739 3740 /// <summary> 3741 3742 /// 把颜色转换为亮度值 3743 3744 /// </summary> 3745 3746 /// <param name="bitmap"></param> 3747 3748 /// <param name="x"></param> 3749 3750 /// <param name="y"></param> 3751 3752 /// <returns></returns> 3753 3754 public static Boolean GetBrightnessByColor(Color curColor, eGrayMode mode, ref Byte brightness) 3755 3756 { 3757 3758 if (curColor == null) 3759 3760 { 3761 3762 return false; 3763 3764 } 3765 3766 3767 3768 //利用公式计算灰度值(加权平均法) 3769 3770 if (mode == eGrayMode.ArithmeticAverage) 3771 3772 { 3773 3774 brightness = (Byte)(curColor.R * 0.299f + curColor.G * 0.587f + curColor.B * 0.114f); 3775 3776 } 3777 3778 else 3779 3780 { 3781 3782 brightness = (Byte)((curColor.R + curColor.G + curColor.B) / 3.0f); 3783 3784 } 3785 3786 return true; 3787 3788 } 3789 3790 3791 3792 #endregion 3793 3794 3795 3796 #region 图片比较 3797 3798 3799 3800 /// <summary> 3801 3802 /// 根据2个图片的亮度值,比较图片的差异部分 3803 3804 /// </summary> 3805 3806 /// <param name="brightnessDiff"></param> 3807 3808 /// <param name="compareret"></param> 3809 3810 /// <returns></returns> 3811 3812 public static Boolean CompareImageBrightness(Byte brightnessDiff, 3813 3814 Byte[] brightness1, Byte[] brightness2, 3815 3816 ref Boolean[] diffPixArray) 3817 3818 { 3819 3820 3821 3822 if (brightness1 == null || brightness2 == null || 3823 3824 brightness1.Length < 1 || brightness2.Length < 1 || 3825 3826 brightness1.Length != brightness2.Length) 3827 3828 { 3829 3830 return false; 3831 3832 } 3833 3834 3835 3836 Int32 arylen = brightness1.Length; 3837 3838 diffPixArray = new Boolean[brightness1.Length]; 3839 3840 Byte bri1 = 0; 3841 3842 Byte bri2 = 0; 3843 3844 3845 3846 for (Int32 byteidx = 0; byteidx < arylen; ++byteidx) 3847 3848 { 3849 3850 bri1 = brightness1[byteidx]; 3851 3852 bri2 = brightness2[byteidx]; 3853 3854 3855 3856 //亮度差超过指定范围 3857 3858 if (bri1 >= bri2 + brightnessDiff || 3859 3860 bri2 >= bri1 + brightnessDiff) 3861 3862 { 3863 3864 diffPixArray[byteidx] = true; 3865 3866 } 3867 3868 } 3869 3870 3871 3872 return true; 3873 3874 } 3875 3876 3877 3878 /// <summary> 3879 3880 /// 把2个图片的尺寸设置为一样大 3881 3882 /// </summary> 3883 3884 /// <param name="image1"></param> 3885 3886 /// <param name="image2"></param> 3887 3888 /// <returns></returns> 3889 3890 public static Boolean ResizeImageToSame(ref Bitmap image1, ref Bitmap image2) 3891 3892 { 3893 3894 if (image1 == null || image2 == null || 3895 3896 image1.Width == 0 || image1.Height == 0 || 3897 3898 image2.Width == 0 || image2.Height == 0) 3899 3900 { 3901 3902 return false; 3903 3904 } 3905 3906 3907 3908 //如果2个图片尺寸不一样,把大的尺寸压缩小了再比较 3909 3910 Boolean bl = false; 3911 3912 Bitmap tmpimg = null; 3913 3914 if (image1.Width > image2.Width && image1.Height < image2.Height) 3915 3916 { 3917 3918 return false; 3919 3920 } 3921 3922 if (image1.Width < image2.Width && image1.Height > image2.Height) 3923 3924 { 3925 3926 return false; 3927 3928 } 3929 3930 3931 3932 //image1 比较大,把image2放大到 与1一样大 3933 3934 if (image1.Width > image2.Width && image1.Height > image2.Height) 3935 3936 { 3937 3938 bl = ResizeImage(image2, image1.Width, image1.Height, ref tmpimg); 3939 3940 image2 = tmpimg; 3941 3942 } 3943 3944 3945 3946 //image 2比较大,把image1放大到 与2一样大 3947 3948 if (image1.Width < image2.Width && image1.Height < image2.Height) 3949 3950 { 3951 3952 bl = ResizeImage(image1, image2.Width, image2.Height, ref tmpimg); 3953 3954 image1 = tmpimg; 3955 3956 } 3957 3958 3959 3960 return true; 3961 3962 } 3963 3964 3965 3966 /// <summary> 3967 3968 /// 根据2个图片的像素颜色值,比较图片的差异部分 3969 3970 /// </summary> 3971 3972 /// <param name="compareparam"></param> 3973 3974 /// <param name="compareret"></param> 3975 3976 /// <returns></returns> 3977 3978 public static Boolean CompareImage(ImageCompareParameter compareparam, 3979 3980 ref ImageCompareResult compareret) 3981 3982 { 3983 3984 Bitmap image1 = compareparam.Image1; 3985 3986 Bitmap image2 = compareparam.Image2; 3987 3988 3989 3990 Int32 briDiff = compareparam.BrightnessDiff; 3991 3992 Color diffColor = compareparam.DifferentAreaFillColor; 3993 3994 Color samecolor = compareparam.SameAreaFillColor; 3995 3996 //是否需要填充相同或不同部分的像素的颜色 3997 3998 Boolean filldiffcolor = (diffColor != Color.Transparent); 3999 4000 Boolean fillsamecolor = (samecolor != Color.Transparent); 4001 4002 4003 4004 //如果图片尺寸不一样,修改为一样大 4005 4006 Boolean bl = ResizeImageToSame(ref image1, ref image2); 4007 4008 if (!bl) 4009 4010 { 4011 4012 return false; 4013 4014 } 4015 4016 4017 4018 Bitmap imagediff = (Bitmap)image1.Clone(); 4019 4020 4021 4022 //不同区域的左上下右位置 4023 4024 Int32 areaLeft = imagediff.Width; 4025 4026 Int32 areaTop = imagediff.Height; 4027 4028 Int32 areaRight = -1; 4029 4030 Int32 areaBottom = -1; 4031 4032 4033 4034 int width = image1.Width; 4035 4036 int height = image1.Height; 4037 4038 4039 4040 long allpixcnt = height * width;//所有像素点数量 4041 4042 long diffpixcnt = 0;//不同像素点数量 4043 4044 long samepixcnt = 0;//相同像素点数量 4045 4046 4047 4048 //3张图片的各像素亮度数组 4049 4050 Int32 briaryidx = 0; 4051 4052 Byte[] briary1 = new Byte[allpixcnt]; 4053 4054 Byte[] briary2 = new Byte[allpixcnt]; 4055 4056 Byte[] briaryret = new Byte[allpixcnt]; 4057 4058 4059 4060 Byte diffB = diffColor.B; 4061 4062 Byte diffG = diffColor.G; 4063 4064 Byte diffR = diffColor.R; 4065 4066 Byte sameB = samecolor.B; 4067 4068 Byte sameG = samecolor.G; 4069 4070 Byte sameR = samecolor.R; 4071 4072 4073 4074 Byte samebri = 0; 4075 4076 Byte diffbri = 0; 4077 4078 GetBrightnessByColor(diffColor, eGrayMode.WeightedAverage, ref samebri); 4079 4080 GetBrightnessByColor(diffColor, eGrayMode.WeightedAverage, ref diffbri); 4081 4082 4083 4084 try 4085 4086 { 4087 4088 4089 4090 BitmapData data1 = image1.LockBits(new Rectangle(0, 0, width, height), 4091 4092 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 4093 4094 4095 4096 BitmapData data2 = image2.LockBits(new Rectangle(0, 0, width, height), 4097 4098 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 4099 4100 4101 4102 BitmapData datadiff = imagediff.LockBits(new Rectangle(0, 0, width, height), 4103 4104 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 4105 4106 4107 4108 byte bri1 = 0; 4109 4110 byte bri2 = 0; 4111 4112 4113 4114 //每个像素是否相同.1相同,0不同 4115 4116 compareret.PixIsDifferent = new Boolean[width * height]; 4117 4118 4119 4120 //当前像素是否不同 4121 4122 Boolean curpixIsdiff = false; 4123 4124 unsafe 4125 4126 { 4127 4128 byte* curpix1 = (byte*)data1.Scan0.ToPointer(); 4129 4130 byte* curpix2 = (byte*)data2.Scan0.ToPointer(); 4131 4132 byte* curpixdiff = (byte*)datadiff.Scan0.ToPointer(); 4133 4134 4135 4136 for (int y = 0; y < height; y++) 4137 4138 { 4139 4140 for (int x = 0; x < width; x++) 4141 4142 { 4143 4144 //利用公式计算灰度值(加权平均法) 4145 4146 //按BGR的顺序存储 4147 4148 bri1 = (Byte)(curpix1[0] * 0.114f + curpix1[1] * 0.587f + curpix1[2] * 0.299f); 4149 4150 bri2 = (Byte)(curpix2[0] * 0.114f + curpix2[1] * 0.587f + curpix2[2] * 0.299f); 4151 4152 4153 4154 //以1作为基准,比较1和2之间的差距,如果超过阀值,认为当前像素有差异 4155 4156 //否则认为当前像素没有差异 4157 4158 curpixIsdiff = false; 4159 4160 if (bri1 >= bri2 + briDiff || 4161 4162 bri2 >= bri1 + briDiff) 4163 4164 { 4165 4166 curpixIsdiff = true; 4167 4168 } 4169 4170 4171 4172 briary1[briaryidx] = bri1; 4173 4174 briary2[briaryidx] = bri2; 4175 4176 4177 4178 if (curpixIsdiff) //如果有差异,设置图像1里的当前像素为 不同颜色 4179 4180 { 4181 4182 if (filldiffcolor) 4183 4184 { 4185 4186 curpixdiff[0] = diffB; 4187 4188 curpixdiff[1] = diffG; 4189 4190 curpixdiff[2] = diffR; 4191 4192 } 4193 4194 ++diffpixcnt; 4195 4196 4197 4198 if (x < areaLeft) //记忆最左边的像素位置 4199 4200 { 4201 4202 areaLeft = x; 4203 4204 } 4205 4206 if (x > areaRight) //记忆最右边的像素位置 4207 4208 { 4209 4210 areaRight = x; 4211 4212 } 4213 4214 if (y < areaTop) //记忆最上边的像素位置 4215 4216 { 4217 4218 areaTop = y; 4219 4220 } 4221 4222 if (y > areaBottom) //记忆最下边的像素位置 4223 4224 { 4225 4226 areaBottom = y; 4227 4228 } 4229 4230 4231 4232 //记忆当前像素的比较结果的亮度 4233 4234 briaryret[briaryidx] = diffbri; 4235 4236 } 4237 4238 else //没有差异,设置结果里的当前像素为 相同颜色 4239 4240 { 4241 4242 if (fillsamecolor) 4243 4244 { 4245 4246 curpixdiff[0] = sameB; 4247 4248 curpixdiff[1] = sameG; 4249 4250 curpixdiff[2] = sameR; 4251 4252 } 4253 4254 ++samepixcnt; 4255 4256 4257 4258 //记忆当前像素的比较结果的亮度 4259 4260 briaryret[briaryidx] = samebri; 4261 4262 } 4263 4264 4265 4266 // 比较结果的亮度数组下标 4267 4268 ++briaryidx; 4269 4270 4271 4272 //像素是否不同的标志 4273 4274 compareret.PixIsDifferent[y * width + x] = curpixIsdiff; 4275 4276 4277 4278 curpix1 += 3; 4279 4280 curpix2 += 3; 4281 4282 curpixdiff += 3; 4283 4284 } 4285 4286 curpix1 += data1.Stride - width * 3; 4287 4288 curpix2 += data1.Stride - width * 3; 4289 4290 curpixdiff += datadiff.Stride - width * 3; 4291 4292 } 4293 4294 } 4295 4296 4297 4298 image1.UnlockBits(data1); 4299 4300 image2.UnlockBits(data2); 4301 4302 imagediff.UnlockBits(datadiff); 4303 4304 4305 4306 compareret.RateDifferent = diffpixcnt / (double)allpixcnt; 4307 4308 compareret.RateSame = samepixcnt / (double)allpixcnt; 4309 4310 4311 4312 compareret.CompareResultImage = imagediff; 4313 4314 compareret.BrightnessDiff = briDiff; 4315 4316 4317 4318 compareret.BrightnessBytesImage1 = briary1; 4319 4320 compareret.BrightnessBytesImage2 = briary2; 4321 4322 compareret.BrightnessBytesResult = briaryret; 4323 4324 4325 4326 //保存区域范围 4327 4328 //compareret.DiffAreaTop = areaTop; 4329 4330 //compareret.DiffAreaLeft = areaLeft; 4331 4332 //compareret.DiffAreaRight = areaRight; 4333 4334 //compareret.DiffAreaBottom = areaBottom; 4335 4336 //compareret.CalculateAreaRate(); 4337 4338 4339 4340 bl = true; 4341 4342 } 4343 4344 catch (Exception ex) 4345 4346 { 4347 4348 Console.WriteLine("CompareImage error:" + ex.Message); 4349 4350 bl = false; 4351 4352 } 4353 4354 4355 4356 return bl; 4357 4358 } 4359 4360 4361 4362 4363 4364 /// <summary> 4365 4366 /// 2张图片亮度值相减,得到新图片以及亮度值 4367 4368 /// </summary> 4369 4370 /// <param name="image1"></param> 4371 4372 /// <param name="image2"></param> 4373 4374 /// <param name="retimage"></param> 4375 4376 /// <param name="brightnessary"></param> 4377 4378 /// <returns></returns> 4379 4380 public static Boolean SubtractImageBrightness(Bitmap image1, Bitmap image2, 4381 4382 ref Bitmap imagediff, ref Byte[] brightnessary) 4383 4384 { 4385 4386 if (image1 == null || image2 == null) 4387 4388 { 4389 4390 return false; 4391 4392 } 4393 4394 4395 4396 Boolean bl = ResizeImageToSame(ref image1, ref image2); 4397 4398 if (!bl) 4399 4400 { 4401 4402 return false; 4403 4404 } 4405 4406 4407 4408 int width = image1.Width; 4409 4410 int height = image1.Height; 4411 4412 4413 4414 long allpixcnt = height * width;//所有像素点数量 4415 4416 4417 4418 brightnessary = new Byte[allpixcnt]; 4419 4420 imagediff = new Bitmap(image1); 4421 4422 Int32 pixidx = 0;//当前像素下标 4423 4424 byte bri1 = 0; 4425 4426 byte bri2 = 0; 4427 4428 BitmapData data1 = null; 4429 4430 BitmapData data2 = null; 4431 4432 BitmapData datadiff = null; 4433 4434 //每行末尾还有这么多的冗余字节 4435 4436 Int32 rowredundancy = 0; 4437 4438 4439 4440 try 4441 4442 { 4443 4444 data1 = image1.LockBits(new Rectangle(0, 0, width, height), 4445 4446 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 4447 4448 4449 4450 data2 = image2.LockBits(new Rectangle(0, 0, width, height), 4451 4452 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 4453 4454 4455 4456 datadiff = imagediff.LockBits(new Rectangle(0, 0, width, height), 4457 4458 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 4459 4460 4461 4462 rowredundancy = datadiff.Stride - width * 3;//每行末尾还有这么多的冗余字节 4463 4464 Byte bridiff = 0; 4465 4466 unsafe 4467 4468 { 4469 4470 byte* curpix1 = (byte*)data1.Scan0.ToPointer(); 4471 4472 byte* curpix2 = (byte*)data2.Scan0.ToPointer(); 4473 4474 byte* cmpretpix = (byte*)datadiff.Scan0.ToPointer(); 4475 4476 4477 4478 for (int y = 0; y < height; y++) 4479 4480 { 4481 4482 for (int x = 0; x < width; x++) 4483 4484 { 4485 4486 bri1 = (byte)((float)(curpix1[0] + curpix1[1] + curpix1[2]) / 3.0f); 4487 4488 bri2 = (byte)((float)(curpix2[0] + curpix2[1] + curpix2[2]) / 3.0f); 4489 4490 4491 4492 bridiff = (bri1 > bri2) ? (Byte)(bri1 - bri2) : (Byte)(bri2 - bri1); //计算当前像素点的亮度值 4493 4494 brightnessary[pixidx] = bridiff;//保存亮度值 4495 4496 ++pixidx; 4497 4498 4499 4500 cmpretpix[0] = bridiff;//把亮度值设置到结果图像里 4501 4502 cmpretpix[1] = bridiff; 4503 4504 cmpretpix[2] = bridiff; 4505 4506 4507 4508 curpix1 += 3; 4509 4510 curpix2 += 3; 4511 4512 cmpretpix += 3; 4513 4514 } 4515 4516 curpix1 += rowredundancy; 4517 4518 curpix2 += rowredundancy; 4519 4520 cmpretpix += rowredundancy; 4521 4522 } 4523 4524 } 4525 4526 4527 4528 image1.UnlockBits(data1); 4529 4530 image2.UnlockBits(data2); 4531 4532 imagediff.UnlockBits(datadiff); 4533 4534 bl = true; 4535 4536 } 4537 4538 catch (Exception ex) 4539 4540 { 4541 4542 Console.WriteLine("CompareImage error:" + ex.Message); 4543 4544 bl = false; 4545 4546 } 4547 4548 4549 4550 return bl; 4551 4552 } 4553 4554 4555 4556 /// <summary> 4557 4558 /// 根据2个图片的亮度值,比较图片的差异部分,并对比较结果的图片执行去噪点处理 4559 4560 /// </summary> 4561 4562 /// <param name="image1"></param> 4563 4564 /// <param name="image2"></param> 4565 4566 /// <param name="bridiff">亮度容差</param> 4567 4568 /// <param name="noisypointsize">噪点边长</param> 4569 4570 /// <param name="imagediff">比较结果的图片</param> 4571 4572 /// <param name="diffary">每个像素是否相同</param> 4573 4574 /// <returns></returns> 4575 4576 public static Boolean CompareImageByBrightness(Bitmap image1, Bitmap image2, 4577 4578 Int32 briDiff, Int32 noisypointsize, 4579 4580 ref Bitmap imagediff, ref Boolean[] diffary) 4581 4582 { 4583 4584 if (image1 == null || image2 == null) 4585 4586 { 4587 4588 return false; 4589 4590 } 4591 4592 4593 4594 Boolean bl = ResizeImageToSame(ref image1, ref image2); 4595 4596 if (!bl) 4597 4598 { 4599 4600 return false; 4601 4602 } 4603 4604 4605 4606 if (briDiff < 1 || briDiff > 255) 4607 4608 { 4609 4610 return false; 4611 4612 } 4613 4614 4615 4616 if (noisypointsize < 1 || noisypointsize * 2 > image1.Height) 4617 4618 { 4619 4620 return false; 4621 4622 } 4623 4624 4625 4626 int width = image1.Width; 4627 4628 int height = image1.Height; 4629 4630 4631 4632 long allpixcnt = height * width;//所有像素点数量 4633 4634 4635 4636 imagediff = new Bitmap(image1); 4637 4638 4639 4640 //每个像素是否相同.1相同,0不同 4641 4642 diffary = new Boolean[width * height]; 4643 4644 4645 4646 Int32 pixidx = 0;//当前像素下标 4647 4648 byte bri1 = 0; 4649 4650 byte bri2 = 0; 4651 4652 BitmapData data1 = null; 4653 4654 BitmapData data2 = null; 4655 4656 BitmapData datadiff = null; 4657 4658 //每行末尾还有这么多的冗余字节 4659 4660 Int32 rowredundancy = 0; 4661 4662 4663 4664 try 4665 4666 { 4667 4668 data1 = image1.LockBits(new Rectangle(0, 0, width, height), 4669 4670 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 4671 4672 4673 4674 data2 = image2.LockBits(new Rectangle(0, 0, width, height), 4675 4676 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 4677 4678 4679 4680 datadiff = imagediff.LockBits(new Rectangle(0, 0, width, height), 4681 4682 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 4683 4684 4685 4686 rowredundancy = datadiff.Stride - width * 3;//每行末尾还有这么多的冗余字节 4687 4688 4689 4690 unsafe 4691 4692 { 4693 4694 byte* curpix1 = (byte*)data1.Scan0.ToPointer(); 4695 4696 byte* curpix2 = (byte*)data2.Scan0.ToPointer(); 4697 4698 byte* cmpretpix = (byte*)datadiff.Scan0.ToPointer(); 4699 4700 4701 4702 for (int y = 0; y < height; y++) 4703 4704 { 4705 4706 for (int x = 0; x < width; x++) 4707 4708 { 4709 4710 bri1 = (byte)((float)(curpix1[0] + curpix1[1] + curpix1[2]) / 3.0f); 4711 4712 bri2 = (byte)((float)(curpix2[0] + curpix2[1] + curpix2[2]) / 3.0f); 4713 4714 4715 4716 //比较2个像素亮度值之差,如果有差异,设置图像1里的当前像素为 不同颜色 4717 4718 if (bri1 >= bri2 + briDiff || 4719 4720 bri2 >= bri1 + briDiff) 4721 4722 { 4723 4724 diffary[pixidx] = true; 4725 4726 cmpretpix[0] = Const_WhiteBrightness; 4727 4728 cmpretpix[1] = Const_WhiteBrightness; 4729 4730 cmpretpix[2] = Const_WhiteBrightness; 4731 4732 } 4733 4734 else 4735 4736 { 4737 4738 diffary[pixidx] = false; 4739 4740 cmpretpix[0] = Const_BlackBrightness; 4741 4742 cmpretpix[1] = Const_BlackBrightness; 4743 4744 cmpretpix[2] = Const_BlackBrightness; 4745 4746 } 4747 4748 4749 4750 ++pixidx; 4751 4752 curpix1 += 3; 4753 4754 curpix2 += 3; 4755 4756 cmpretpix += 3; 4757 4758 } 4759 4760 curpix1 += rowredundancy; 4761 4762 curpix2 += rowredundancy; 4763 4764 cmpretpix += rowredundancy; 4765 4766 } 4767 4768 } 4769 4770 4771 4772 image1.UnlockBits(data1); 4773 4774 image2.UnlockBits(data2); 4775 4776 imagediff.UnlockBits(datadiff); 4777 4778 bl = true; 4779 4780 } 4781 4782 catch (Exception ex) 4783 4784 { 4785 4786 Console.WriteLine("CompareImage error:" + ex.Message); 4787 4788 bl = false; 4789 4790 } 4791 4792 4793 4794 //现在对图像执行去噪点处理 4795 4796 RemoveNoisypoint(noisypointsize, ref imagediff); 4797 4798 4799 4800 //获取去除噪点后各像素亮度 4801 4802 Byte pixbri = 0;//当前像素亮度 4803 4804 pixidx = 0; 4805 4806 try 4807 4808 { 4809 4810 datadiff = imagediff.LockBits(new Rectangle(0, 0, width, height), 4811 4812 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 4813 4814 unsafe 4815 4816 { 4817 4818 byte* cmpretpix = (byte*)datadiff.Scan0.ToPointer(); 4819 4820 4821 4822 for (int y = 0; y < height; y++) 4823 4824 { 4825 4826 for (int x = 0; x < width; x++) 4827 4828 { 4829 4830 pixbri = (byte)((float)(cmpretpix[0] + cmpretpix[1] + cmpretpix[2]) / 3.0f); 4831 4832 4833 4834 //去除噪点后,已经变得非黑即白.如果是黑色,表示相同,白色,表示不同 4835 4836 diffary[pixidx] = (pixbri > briDiff); 4837 4838 ++pixidx; 4839 4840 cmpretpix += 3; 4841 4842 } 4843 4844 cmpretpix += rowredundancy; 4845 4846 } 4847 4848 } 4849 4850 4851 4852 imagediff.UnlockBits(datadiff); 4853 4854 bl = true; 4855 4856 } 4857 4858 catch (Exception ex) 4859 4860 { 4861 4862 Console.WriteLine("CompareImage error:" + ex.Message); 4863 4864 bl = false; 4865 4866 } 4867 4868 return bl; 4869 4870 } 4871 4872 4873 4874 /// 根据2个图片的亮度值,比较图片的差异部分 4875 4876 /// </summary> 4877 4878 /// <param name="compareparam"></param> 4879 4880 /// <param name="compareret"></param> 4881 4882 /// <returns></returns> 4883 4884 public static Boolean CompareImageByBrightness(ImageCompareParameter compareparam, 4885 4886 ref ImageCompareResult compareret) 4887 4888 { 4889 4890 Bitmap image1 = compareparam.Image1; 4891 4892 Bitmap image2 = compareparam.Image2; 4893 4894 4895 4896 Byte[] imagebri1 = compareparam.BrightnessBytesImage1; 4897 4898 Byte[] imagebri2 = compareparam.BrightnessBytesImage2; 4899 4900 4901 4902 Int32 briDiff = compareparam.BrightnessDiff; 4903 4904 Color diffColor = compareparam.DifferentAreaFillColor; 4905 4906 Color samecolor = compareparam.SameAreaFillColor; 4907 4908 //是否需要填充相同或不同部分的像素的颜色 4909 4910 Boolean filldiffcolor = (diffColor != Color.Transparent); 4911 4912 Boolean fillsamecolor = (samecolor != Color.Transparent); 4913 4914 4915 4916 Boolean bl = false; 4917 4918 4919 4920 Bitmap imagediff = new Bitmap(image1); 4921 4922 4923 4924 //不同区域的左上下右位置 4925 4926 Int32 areaLeft = imagediff.Width; 4927 4928 Int32 areaTop = imagediff.Height; 4929 4930 Int32 areaRight = -1; 4931 4932 Int32 areaBottom = -1; 4933 4934 4935 4936 int width = image1.Width; 4937 4938 int height = image1.Height; 4939 4940 4941 4942 long allpixcnt = height * width;//所有像素点数量 4943 4944 long diffpixcnt = 0;//不同像素点数量 4945 4946 long samepixcnt = 0;//相同像素点数量 4947 4948 4949 4950 if (imagebri1 == null || imagebri2 == null || 4951 4952 imagebri2.Length != imagebri2.Length || 4953 4954 imagebri2.Length != allpixcnt) 4955 4956 { 4957 4958 return false; 4959 4960 } 4961 4962 4963 4964 //3张图片的各像素亮度数组 4965 4966 Int32 briaryidx = 0; 4967 4968 Byte[] briaryret = new Byte[allpixcnt]; 4969 4970 4971 4972 Byte diffB = diffColor.B; 4973 4974 Byte diffG = diffColor.G; 4975 4976 Byte diffR = diffColor.R; 4977 4978 Byte sameB = samecolor.B; 4979 4980 Byte sameG = samecolor.G; 4981 4982 Byte sameR = samecolor.R; 4983 4984 4985 4986 Byte samebri = 0; 4987 4988 Byte diffbri = 0; 4989 4990 GetBrightnessByColor(diffColor, eGrayMode.WeightedAverage, ref samebri); 4991 4992 GetBrightnessByColor(diffColor, eGrayMode.WeightedAverage, ref diffbri); 4993 4994 4995 4996 Int32 currowfirstx = 0;//当前行的首个像素的下标 4997 4998 try 4999 5000 { 5001 5002 5003 5004 BitmapData data1 = image1.LockBits(new Rectangle(0, 0, width, height), 5005 5006 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 5007 5008 5009 5010 BitmapData data2 = image2.LockBits(new Rectangle(0, 0, width, height), 5011 5012 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 5013 5014 5015 5016 BitmapData datadiff = imagediff.LockBits(new Rectangle(0, 0, width, height), 5017 5018 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 5019 5020 5021 5022 byte bri1 = 0; 5023 5024 byte bri2 = 0; 5025 5026 5027 5028 //每个像素是否相同.1相同,0不同 5029 5030 compareret.PixIsDifferent = new Boolean[width * height]; 5031 5032 5033 5034 //当前像素是否不同 5035 5036 Boolean curpixIsdiff = false; 5037 5038 unsafe 5039 5040 { 5041 5042 byte* curpix1 = (byte*)data1.Scan0.ToPointer(); 5043 5044 byte* curpix2 = (byte*)data2.Scan0.ToPointer(); 5045 5046 byte* cmpretpix = (byte*)datadiff.Scan0.ToPointer(); 5047 5048 5049 5050 for (int y = 0; y < height; y++) 5051 5052 { 5053 5054 currowfirstx = y * width; 5055 5056 for (int x = 0; x < width; x++) 5057 5058 { 5059 5060 bri1 = imagebri1[currowfirstx + x]; 5061 5062 bri2 = imagebri2[currowfirstx + x]; 5063 5064 5065 5066 //以1作为基准,比较1和2之间的差距,如果超过阀值,认为当前像素有差异 5067 5068 //否则认为当前像素没有差异 5069 5070 curpixIsdiff = false; 5071 5072 if (bri1 >= bri2 + briDiff || 5073 5074 bri2 >= bri1 + briDiff) 5075 5076 { 5077 5078 curpixIsdiff = true; 5079 5080 } 5081 5082 5083 5084 if (curpixIsdiff) //如果有差异,设置图像1里的当前像素为 不同颜色 5085 5086 { 5087 5088 if (filldiffcolor) 5089 5090 { 5091 5092 cmpretpix[0] = diffB; 5093 5094 cmpretpix[1] = diffG; 5095 5096 cmpretpix[2] = diffR; 5097 5098 } 5099 5100 ++diffpixcnt; 5101 5102 5103 5104 if (x < areaLeft) //记忆最左边的像素位置 5105 5106 { 5107 5108 areaLeft = x; 5109 5110 } 5111 5112 if (x > areaRight) //记忆最右边的像素位置 5113 5114 { 5115 5116 areaRight = x; 5117 5118 } 5119 5120 if (y < areaTop) //记忆最上边的像素位置 5121 5122 { 5123 5124 areaTop = y; 5125 5126 } 5127 5128 if (y > areaBottom) //记忆最下边的像素位置 5129 5130 { 5131 5132 areaBottom = y; 5133 5134 } 5135 5136 5137 5138 //记忆当前像素的比较结果的亮度 5139 5140 briaryret[briaryidx] = diffbri; 5141 5142 } 5143 5144 else //没有差异,设置结果里的当前像素为 相同颜色 5145 5146 { 5147 5148 if (fillsamecolor) 5149 5150 { 5151 5152 cmpretpix[0] = sameB; 5153 5154 cmpretpix[1] = sameG; 5155 5156 cmpretpix[2] = sameR; 5157 5158 } 5159 5160 ++samepixcnt; 5161 5162 5163 5164 //记忆当前像素的比较结果的亮度 5165 5166 briaryret[briaryidx] = samebri; 5167 5168 } 5169 5170 5171 5172 // 比较结果的亮度数组下标 5173 5174 ++briaryidx; 5175 5176 5177 5178 //像素是否不同的标志 5179 5180 compareret.PixIsDifferent[currowfirstx + x] = curpixIsdiff; 5181 5182 5183 5184 curpix1 += 3; 5185 5186 curpix2 += 3; 5187 5188 cmpretpix += 3; 5189 5190 } 5191 5192 curpix1 += data1.Stride - width * 3; 5193 5194 curpix2 += data1.Stride - width * 3; 5195 5196 cmpretpix += datadiff.Stride - width * 3; 5197 5198 } 5199 5200 } 5201 5202 5203 5204 image1.UnlockBits(data1); 5205 5206 image2.UnlockBits(data2); 5207 5208 imagediff.UnlockBits(datadiff); 5209 5210 5211 5212 compareret.RateDifferent = diffpixcnt / (double)allpixcnt; 5213 5214 compareret.RateSame = samepixcnt / (double)allpixcnt; 5215 5216 5217 5218 compareret.CompareResultImage = imagediff; 5219 5220 compareret.BrightnessDiff = briDiff; 5221 5222 compareret.BrightnessBytesResult = briaryret; 5223 5224 5225 5226 bl = true; 5227 5228 } 5229 5230 catch (Exception ex) 5231 5232 { 5233 5234 Console.WriteLine("CompareImage error:" + ex.Message); 5235 5236 bl = false; 5237 5238 } 5239 5240 5241 5242 return bl; 5243 5244 } 5245 5246 5247 5248 /// <summary> 5249 5250 /// 获取一个区域的实际坐标 5251 5252 /// </summary> 5253 5254 /// <param name="area"></param> 5255 5256 /// <param name="width"></param> 5257 5258 /// <param name="height"></param> 5259 5260 /// <param name="x1"></param> 5261 5262 /// <param name="y1"></param> 5263 5264 /// <param name="x2"></param> 5265 5266 /// <param name="y2"></param> 5267 5268 public static void GetAreaPositionInImage(ImageAreaInfo area, 5269 5270 Int32 width, Int32 height, 5271 5272 ref Int32 x1, ref Int32 y1, ref Int32 x2, ref Int32 y2) 5273 5274 { 5275 5276 if (area.PositionType == ePositionType.ByPix) 5277 5278 { 5279 5280 x1 = (Int32)area.X1; 5281 5282 y1 = (Int32)area.Y1; 5283 5284 x2 = (Int32)area.X2; 5285 5286 y2 = (Int32)area.Y2; 5287 5288 } 5289 5290 else 5291 5292 { 5293 5294 x1 = (Int32)(area.X1 * (double)width); 5295 5296 y1 = (Int32)(area.Y1 * (double)height); 5297 5298 x2 = (Int32)(area.X2 * (double)width); 5299 5300 y2 = (Int32)(area.Y2 * (double)height); 5301 5302 } 5303 5304 } 5305 5306 5307 5308 /// <summary> 5309 5310 /// 检查指定区域的图像是否与方案里的指定值一样(都是相同或者不同) 5311 5312 /// </summary> 5313 5314 /// <param name="briDiffary">每个元素对应2张图片的每个像素亮度相同还是不同.true不同,false相同</param> 5315 5316 /// <param name="area"></param> 5317 5318 /// <returns></returns> 5319 5320 public static Boolean ValidateImageArea(Boolean[] briDiffary, ImageAreaInfo area, Int32 width, Int32 height) 5321 5322 { 5323 5324 if (briDiffary == null || briDiffary.Length < 4 || area == null || 5325 5326 width < 1 || height < 1 || width * height != briDiffary.Length) 5327 5328 { 5329 5330 return false; 5331 5332 } 5333 5334 5335 5336 Int32 x1 = 0; 5337 5338 Int32 x2 = 0; 5339 5340 Int32 y1 = 0; 5341 5342 Int32 y2 = 0; 5343 5344 5345 5346 //获取该区域在图像里的实际坐标范围 5347 5348 GetAreaPositionInImage(area, width, height, 5349 5350 ref x1, ref y1, ref x2, ref y2); 5351 5352 5353 5354 //获取该区域里的像素匹配类型 5355 5356 eAreaDifferentType difftype = eAreaDifferentType.Partial; 5357 5358 Boolean bl = GetImageAreaDifferentType(briDiffary, width, height, 5359 5360 x1, y1, x2, y2, ref difftype); 5361 5362 if (!bl) 5363 5364 { 5365 5366 return false; 5367 5368 } 5369 5370 5371 5372 //如果是期待所有像素都是相同,要求必须每个像素都相同.任何一个不同,就认为失败 5373 5374 if (area.ExpectDispMode == eDrawType.ExpectHide && 5375 5376 difftype != eAreaDifferentType.AllSame) 5377 5378 { 5379 5380 return false; 5381 5382 } 5383 5384 5385 5386 //如果是期待像素不同,只要有1个像素不同就可以.所有像素都相同,认为失败 5387 5388 if (area.ExpectDispMode == eDrawType.ExpectShow && 5389 5390 difftype == eAreaDifferentType.AllSame) 5391 5392 { 5393 5394 return false; 5395 5396 } 5397 5398 return true; 5399 5400 } 5401 5402 5403 5404 /// <summary> 5405 5406 /// 检查指定区域的图像是否与方案里的指定值一样(都是相同或者不同) 5407 5408 /// </summary> 5409 5410 /// <param name="pixDiffary"></param> 5411 5412 /// <param name="area"></param> 5413 5414 /// <returns></returns> 5415 5416 public static Boolean ValidateImageArea(Byte[] briDiffary, ImageAreaInfo area, Int32 width, Int32 height) 5417 5418 { 5419 5420 5421 5422 Boolean[] blary = new Boolean[briDiffary.Length]; 5423 5424 for (Int32 idx = 0; idx < briDiffary.Length; ++idx) 5425 5426 { 5427 5428 blary[idx] = (briDiffary[idx] > 0); 5429 5430 } 5431 5432 5433 5434 Boolean bl = ValidateImageArea(blary, area, width, height); 5435 5436 return bl; 5437 5438 } 5439 5440 5441 5442 /// <summary> 5443 5444 /// 检查图片的比较结果里,某个区域是否与期待的一致 5445 5446 /// </summary> 5447 5448 /// <param name="compareret"></param> 5449 5450 /// <param name="area"></param> 5451 5452 /// <returns>true-与期待一致;false-不一致</returns> 5453 5454 public static Boolean ValidateImageArea(ImageCompareResult compareret, ImageAreaInfo area) 5455 5456 { 5457 5458 Boolean[] pixDiffary = compareret.PixIsDifferent; 5459 5460 5461 5462 Bitmap tmp = new Bitmap(compareret.CompareResultImage); 5463 5464 Int32 width = tmp.Width; 5465 5466 Int32 height = tmp.Height; 5467 5468 Boolean bl = ValidateImageArea(compareret.PixIsDifferent, area, width, height); 5469 5470 5471 5472 return bl; 5473 5474 } 5475 5476 5477 5478 /// <summary> 5479 5480 /// 获取1个 比较结果里,指定的区域范围,是全都相同,还是不同 5481 5482 /// 只有所有像素都是相同,才认为是整个区域相同 5483 5484 /// 如果有1个像素不同,则认为整个区域不同 5485 5486 /// </summary> 5487 5488 /// <param name="pixDiffary"></param> 5489 5490 /// <param name="width"></param> 5491 5492 /// <param name="height"></param> 5493 5494 /// <param name="startX"></param> 5495 5496 /// <param name="startY"></param> 5497 5498 /// <param name="endX"></param> 5499 5500 /// <param name="endY"></param> 5501 5502 /// <returns> </returns> 5503 5504 public static Boolean GetImageAreaDifferentType(Boolean[] pixDiffary, Int32 width, Int32 height, 5505 5506 Int32 x1, Int32 y1, Int32 x2, Int32 y2, ref eAreaDifferentType difftype) 5507 5508 { 5509 5510 Int32 areawidth = x2 - x1; 5511 5512 Int32 areaheight = y2 - y1; 5513 5514 5515 5516 if (pixDiffary == null || width < 1 || height < 1 || 5517 5518 areawidth < 1 || areaheight < 1 || 5519 5520 width < areawidth || height < areaheight || 5521 5522 pixDiffary.Length < width * height) 5523 5524 { 5525 5526 return false; 5527 5528 } 5529 5530 5531 5532 Boolean allissame = false; //假设所有像素相同 5533 5534 Boolean allisdiff = false; //假设所有像素不同 5535 5536 5537 5538 Int32 currowFirstPix = 0; 5539 5540 for (Int32 y = y1; y <= y2; ++y) 5541 5542 { 5543 5544 currowFirstPix = y * width; 5545 5546 for (Int32 x = x1; x <= x2; ++x) 5547 5548 { 5549 5550 if (pixDiffary[currowFirstPix + x]) //当前像素点不同 5551 5552 { 5553 5554 allisdiff = true; 5555 5556 } 5557 5558 else//当前像素相同 5559 5560 { 5561 5562 allissame = true; 5563 5564 } 5565 5566 5567 5568 //如果已经有部分相同部分不同,退出 5569 5570 if (allisdiff && allissame) 5571 5572 { 5573 5574 difftype = eAreaDifferentType.Partial; 5575 5576 return true; 5577 5578 } 5579 5580 } 5581 5582 } 5583 5584 5585 5586 //现在,所有像素都相同,或都不同 5587 5588 if (allisdiff) 5589 5590 { 5591 5592 difftype = eAreaDifferentType.AllDifferent; 5593 5594 } 5595 5596 else 5597 5598 { 5599 5600 difftype = eAreaDifferentType.AllSame; 5601 5602 } 5603 5604 return true; 5605 5606 } 5607 5608 5609 5610 /// <summary> 5611 5612 /// 根据亮度容差,把图片转换为非黑即白的图片 5613 5614 /// </summary> 5615 5616 /// <param name="briimg"></param> 5617 5618 /// <param name="brigate"></param> 5619 5620 /// <returns></returns> 5621 5622 public static Boolean GetBlackWhiteImage(Bitmap briimg, Byte[] briDiffary, Int32 brigate, ref Bitmap blackwhiteimage) 5623 5624 { 5625 5626 if (briimg == null) 5627 5628 { 5629 5630 return false; 5631 5632 } 5633 5634 5635 5636 int width = briimg.Width; 5637 5638 int height = briimg.Height; 5639 5640 5641 5642 long allpixcnt = height * width;//所有像素点数量 5643 5644 5645 5646 if (briDiffary == null || briDiffary.Length != allpixcnt) 5647 5648 { 5649 5650 return false; 5651 5652 } 5653 5654 5655 5656 blackwhiteimage = new Bitmap(briimg); 5657 5658 Int32 pixidx = 0;//当前像素下标 5659 5660 BitmapData datasrc = null; 5661 5662 BitmapData dataret = null; 5663 5664 5665 5666 //每行末尾还有这么多的冗余字节 5667 5668 Int32 rowredundancy = 0; 5669 5670 5671 5672 Byte curpixBri = 0;//当前的亮度 5673 5674 try 5675 5676 { 5677 5678 datasrc = briimg.LockBits(new Rectangle(0, 0, width, height), 5679 5680 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 5681 5682 5683 5684 dataret = blackwhiteimage.LockBits(new Rectangle(0, 0, width, height), 5685 5686 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 5687 5688 5689 5690 rowredundancy = datasrc.Stride - width * 3;//每行末尾还有这么多的冗余字节 5691 5692 unsafe 5693 5694 { 5695 5696 byte* pixret = (byte*)dataret.Scan0.ToPointer(); 5697 5698 5699 5700 for (int y = 0; y < height; y++) 5701 5702 { 5703 5704 for (int x = 0; x < width; x++) 5705 5706 { 5707 5708 //亮度差值大于门限的,认为是不同部分,用白色填充 5709 5710 curpixBri = (briDiffary[pixidx] > brigate) ? Const_BrightnessWhite : Const_BrightnessBlack; 5711 5712 pixret[0] = curpixBri;//把亮度值设置到结果图像里 5713 5714 pixret[1] = curpixBri; 5715 5716 pixret[2] = curpixBri; 5717 5718 ++pixidx; 5719 5720 pixret += 3; 5721 5722 } 5723 5724 pixret += rowredundancy; 5725 5726 } 5727 5728 } 5729 5730 5731 5732 briimg.UnlockBits(datasrc); 5733 5734 blackwhiteimage.UnlockBits(dataret); 5735 5736 } 5737 5738 catch (Exception ex) 5739 5740 { 5741 5742 Console.WriteLine("GetBlackWhiteImage error:" + ex.Message); 5743 5744 return false; 5745 5746 } 5747 5748 5749 5750 return true; 5751 5752 } 5753 5754 #endregion 5755 5756 5757 5758 #region 内部实现 5759 5760 5761 5762 5763 5764 /// <summary> 5765 5766 /// 比较2个数值之间的差是否大于指定值 5767 5768 /// </summary> 5769 5770 /// <param name="val1"></param> 5771 5772 /// <param name="val2"></param> 5773 5774 /// <param name="diff"></param> 5775 5776 /// <returns>超过指定值返回true;否则返回false</returns> 5777 5778 private static Boolean CheckDiffOver(Int32 val1, Int32 val2, Int32 diff) 5779 5780 { 5781 5782 if (diff < 1) 5783 5784 { 5785 5786 return false; 5787 5788 } 5789 5790 if (val1 > val2 && val1 > val2 + diff) 5791 5792 { 5793 5794 return true; 5795 5796 } 5797 5798 if (val2 > val1 && val2 > val1 + diff) 5799 5800 { 5801 5802 return true; 5803 5804 } 5805 5806 return false; 5807 5808 } 5809 5810 #endregion 5811 5812 } 5813 5814 }