由於公司業務需求,須要在Windows系統下調用攝像頭識別二維碼需求,就有了這個功能。git
我根據網上網友提供的一些資料,本身整合應用到項目中,效果還不錯(就是感受像素不是太好)github
如今將調用攝像頭+識別二維碼這兩個功能單獨出來寫到這裏,供你們討論和參考。多線程
有什麼不足或者問題你們能夠提出來,共同改進共同進步ide
建立一個空的winform項目解決方案,我起名叫他:ScanQRCode測試
將Form1做爲主窗體,設置相關屬性:this
StartPosition:CenterScreen (窗體居中)spa
添加一個居中標題:線程
1 private void LoadTitleCenterData() 2 { 3 string titleMsg ="二維碼識別主界面"; 4 Graphics g = this.CreateGraphics(); 5 Double startingPoint = (this.Width / 2) - (g.MeasureString(titleMsg, this.Font).Width / 2); 6 Double widthOfASpace = g.MeasureString(" ", this.Font).Width; 7 String tmp = " "; 8 Double tmpWidth = 0; 9 10 while ((tmpWidth + widthOfASpace) < startingPoint) 11 { 12 tmp += " "; 13 tmpWidth += widthOfASpace; 14 } 15 this.Text = tmp + titleMsg; 16 }
最大最小化禁用:code
1 public Form1() 2 { 3 this.MinimizeBox = false; 4 this.MaximizeBox = false; 5 InitializeComponent(); 6 LoadTitleCenterData(); 7 }
Form1中添加一個TableLayoutPanel,三行三列,比例按照百分比:10%,80%,10%這樣orm
在TableLayoutPanel的80%中再添加一個TableLayoutPanel,仍是行比例:20%,80%這樣(二八定律)
在TableLayoutPanel中添加Panel,在其中手動在添加幾個按鈕和label
最終界面這樣(能看就行):
添加一個二維碼識別界面CameraQR:
使用Nuget添加引用,搜索AForge,將以下程序包引入:
添加一個識別二維碼的窗體,命名名稱爲:CameraQR
將VideoSourcePlayer添加到窗體中,Fill顯示:
窗體中定義幾個私有變量:
1 private AForge.Video.DirectShow.FilterInfoCollection _videoDevices;//攝像設備 2 System.Timers.Timer timer;//定時器 3 CameraHelper _cameraHelper = new CameraHelper();//視屏設備操做類
窗體Load事件中獲取拍照設備列表,並將第一個設備做爲攝像設備(若有先後兩個或多個攝像頭,本身去改一下代碼,設置成能夠選擇的,在CameraHelper中的CreateFilterInfoCollection()中):
1 private void CameraQR_Load(object sender, EventArgs e) 2 { 3 // 獲取視頻輸入設備 4 _videoDevices = _cameraHelper.CreateFilterInfoCollection();//獲取拍照設備列表 5 if (_videoDevices.Count == 0) 6 { 7 MessageBox.Show("無設備"); 8 this.Dispose(); 9 this.Close(); 10 return; 11 } 12 resultStr = "";//二維碼識別字符串清空 13 _cameraHelper.ConnectDevice(videoSourcePlayer1);//鏈接打開設備 14 }
組件初始化完成以後,添加一個定時任務,用來階段性識別攝像設備中的圖片資源,我寫的是每200毫秒去識別一次,若是圖片中有二維碼,就識別二維碼;識別成功以後,關閉窗體,將識別結果返回給上一個界面,此處須要一個有識別二維碼程序包
使用Nuget添加引用,搜索ZXing,將以下程序包引入:
代碼以下(核心代碼基本就這些):
public CameraQR() { this.MinimizeBox = false; this.MaximizeBox = false; InitializeComponent(); LoadTitleCenterData(); CheckForIllegalCrossThreadCalls = false;//多線程中訪問窗體控件資源不會異常 AddTimer();//定時識別圖片 } private void AddTimer() { timer = new System.Timers.Timer(); timer.Enabled = true; timer.Interval = 200; timer.Start(); timer.Elapsed += new ElapsedEventHandler(PicToQRCode); }
private void PicToQRCode(object sender, ElapsedEventArgs e) { if (_cameraHelper.img == null) return; BinaryBitmap bitmap = null; try { MemoryStream ms = new MemoryStream(); _cameraHelper.img.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); byte[] bt = ms.GetBuffer(); ms.Close(); LuminanceSource source = new RGBLuminanceSource(bt, _cameraHelper.img.Width, _cameraHelper.img.Height); bitmap = new BinaryBitmap(new ZXing.Common.HybridBinarizer(source)); } catch (Exception ex) { return; } Result result=null; try { //開始解碼 result = new MultiFormatReader().decode(bitmap); } catch (ReaderException ex) { resultStr = ex.ToString(); } if (result != null) { resultStr = result.Text; this.DialogResult = DialogResult.OK; this.Close(); }
}
窗體關閉時,記得釋放定時器 關閉攝像頭(否則異常滿天飛):
private void CameraQR_FormClosing(object sender, FormClosingEventArgs e) { if (timer != null) { timer.Dispose(); } _cameraHelper.CloseDevice(); }
CameraHelper類:
1 public class CameraHelper 2 { 3 public FilterInfoCollection _videoDevices;//本機攝像硬件設備列表 4 public VideoSourcePlayer _videoSourcePlayer;//視頻畫布 5 public Bitmap img = null;//全局變量,保存每一次捕獲的圖像 6 public System.Drawing.Image CaptureImage(VideoSourcePlayer sourcePlayer = null) 7 { 8 9 if (sourcePlayer == null || sourcePlayer.VideoSource == null) 10 { 11 if (_videoSourcePlayer == null) 12 return null; 13 else 14 { 15 sourcePlayer = _videoSourcePlayer; 16 } 17 } 18 19 try 20 { 21 if (sourcePlayer.IsRunning) 22 { 23 System.Drawing.Image bitmap = sourcePlayer.GetCurrentVideoFrame(); 24 return bitmap; 25 } 26 return null; 27 28 } 29 catch (Exception ex) 30 { 31 return null; 32 } 33 } 34 35 public FilterInfoCollection CreateFilterInfoCollection() 36 { 37 if (_videoDevices != null) 38 return _videoDevices; 39 _videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice); 40 return _videoDevices; 41 } 42 43 public VideoCaptureDevice ConnectDevice(VideoSourcePlayer videoSourcePlayer, FilterInfo filterInfo = null) 44 { 45 VideoCaptureDevice videoSource = new VideoCaptureDevice(); 46 if (filterInfo == null) 47 { 48 videoSource = new VideoCaptureDevice(_videoDevices[_videoDevices.Count - 1].MonikerString); 49 } 50 else 51 { 52 videoSource = new VideoCaptureDevice(filterInfo.MonikerString); 53 } 54 55 videoSource.NewFrame += new NewFrameEventHandler(video_NewFrame); 56 videoSourcePlayer.VideoSource = videoSource; 57 videoSourcePlayer.Start(); 58 _videoSourcePlayer = videoSourcePlayer; 59 return videoSource; 60 } 61 62 private void video_NewFrame(object sender, NewFrameEventArgs eventArgs) 63 { 64 img = (Bitmap)eventArgs.Frame.Clone(); 65 } 66 67 public void CloseDevice(VideoSourcePlayer videoSourcePlayer = null) 68 { 69 if (videoSourcePlayer == null) 70 { 71 if (_videoSourcePlayer == null) 72 return; 73 _videoSourcePlayer.SignalToStop(); 74 } 75 else 76 { 77 videoSourcePlayer.SignalToStop(); 78 } 79 } 80 }
我用的測試二維碼是:
最終的別結果爲:
代碼:https://github.com/Binzm/ScanQRCode.git