Dynamsoft Barcode SDK安装包里自带了一个功能强大的扫码Demo,这个Demo支持的条形码扫描功能包括文件读取,扫描仪图像读取,以及摄像头视频流读取。
然而扫描仪和摄像头的调用功能并不是免费的,需要用到Dynamic .NET TWAIN这个商用SDK。这篇文章分享下如何去掉扫描仪功能,并把Webcam调用接口替换成DirectShow。
Windows桌面条形码扫描应用
安装Dynamsoft Barcode Reader之后,找到工程<Dynamsoft Barcode Reader>\Samples\Desktop\C#\BarcodeReaderDemo
,并导入Visual Studio
。
这个工程依赖的DLL包括Dynamsoft.BarcodeReader.dll, Dynamsoft.ImageCore.dll,Dynamsoft.Forms.Viewer.dll, Dynamsoft.Camera.dll,Dynamsoft.PDF.dll,Dynamsoft.Twain.dll。
扫码工程修改
去掉扫描仪和摄像头依赖的DLL:Dynamsoft.Camera.dll, Dynamsoft.PDF.dll ,Dynamsoft.Twain.dll。
在App.config
文件中去掉key ="DNTLicense" value ="LICENSE-KEY" :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key ="DBRLicense" value ="LICENSE-KEY"/>
<add key ="DNTLicense" value ="LICENSE-KEY"/>
</appSettings>
</configuration>
在BarcodeReaderDemo.cs
中去掉scanner选项:
// remove
mRoundedRectanglePanelAcquireLoad.Controls.Add(mThAcquireImage);
调整一下UI:
// before
mThLoadImage.Size = new Size(103, 40);
mThWebCamImage.Location = new Point(207, 1);
mThWebCamImage.Size = new Size(103, 40);
// after
mThLoadImage.Size = new Size(156, 40);
mThWebCamImage.Location = new Point(157, 1);
mThWebCamImage.Size = new Size(156, 40);
删除Dynamic .NET TWAIN相关代码:
// remove
mTwainManager = new TwainManager(dntLicenseKeys);
mCameraManager = new CameraManager(dntLicenseKeys);
mPDFRasterizer = new PDFRasterizer(dntLicenseKeys);
…
Build一下工程会报出相关的错误,把对应的代码都删除。
调整后的界面:
使用DirectShowNet控制Webcam
创建一个DSManager.cs
用于控制DirectShow的逻辑。
定义两个结构体存放摄像头相关的参数:
public struct Resolution
{
public Resolution(int width, int height)
{
Width = width;
Height = height;
}
public int Width { get; }
public int Height { get; }
public override string ToString() => $"({Width} x {Height})";
}
public struct CameraInfo
{
public CameraInfo(DsDevice device, List<Resolution> resolutions)
{
Device = device;
Resolutions = resolutions;
}
public DsDevice Device { get; }
public List<Resolution> Resolutions { get; }
}
枚举所有的摄像头:
DsDevice[] devices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
if (devices != null)
{
cameras = new List<CameraInfo>();
foreach (DsDevice device in devices)
{
List<Resolution> resolutions = GetAllAvailableResolution(device);
cameras.Add(new CameraInfo(device, resolutions));
}
}
查找每个摄像头对应的分辨率:
private List<Resolution> GetAllAvailableResolution(DsDevice vidDev)
{
try
{
int hr, bitCount = 0;
IBaseFilter sourceFilter = null;
var m_FilterGraph2 = new FilterGraph() as IFilterGraph2;
hr = m_FilterGraph2.AddSourceFilterForMoniker(vidDev.Mon, null, vidDev.Name, out sourceFilter);
var pRaw2 = DsFindPin.ByCategory(sourceFilter, PinCategory.Capture, 0);
var AvailableResolutions = new List<Resolution>();
VideoInfoHeader v = new VideoInfoHeader();
IEnumMediaTypes mediaTypeEnum;
hr = pRaw2.EnumMediaTypes(out mediaTypeEnum);
AMMediaType[] mediaTypes = new AMMediaType[1];
IntPtr fetched = IntPtr.Zero;
hr = mediaTypeEnum.Next(1, mediaTypes, fetched);
while (fetched != null && mediaTypes[0] != null)
{
Marshal.PtrToStructure(mediaTypes[0].formatPtr, v);
if (v.BmiHeader.Size != 0 && v.BmiHeader.BitCount != 0)
{
if (v.BmiHeader.BitCount > bitCount)
{
AvailableResolutions.Clear();
bitCount = v.BmiHeader.BitCount;
}
AvailableResolutions.Add(new Resolution(v.BmiHeader.Width, v.BmiHeader.Height));
}
hr = mediaTypeEnum.Next(1, mediaTypes, fetched);
}
return AvailableResolutions;
}
catch (Exception ex)
{
//MessageBox.Show(ex.Message);
Console.WriteLine(ex.ToString());
return new List<Resolution>();
}
}
把摄像头的名字和分辨率绑定到UI上显示。当设置UI索引的时候,对应的事件函数会被触发:
private void InitCameraSource()
{
cbxWebCamSrc.Items.Clear();
foreach (CameraInfo camera in mDSManager.GetCameras())
{
cbxWebCamSrc.Items.Add(camera.Device.Name);
}
cbxWebCamSrc.SelectedIndex = 0;
}
private void cbxWebCamSrc_SelectedIndexChanged(object sender, EventArgs e)
{
picBoxWebCam.Visible = true;
picBoxWebCam.BringToFront();
EnableControls(picboxReadBarcode);
EnableControls(pictureBoxCustomize);
InitCameraResolution();
}
private void InitCameraResolution()
{
cbxWebCamRes.Items.Clear();
foreach (Resolution resolution in mDSManager.GetCameras()[cbxWebCamSrc.SelectedIndex].Resolutions)
{
cbxWebCamRes.Items.Add(resolution.ToString());
}
cbxWebCamRes.SelectedIndex = 0;
}
设置回调函数用于接收视频帧做条形码识别:
TaskCompletedCallBack callback = FrameCallback;
private volatile bool isFinished = true;
public void FrameCallback(Bitmap bitmap)
{
if (isFinished)
{
this.BeginInvoke((MethodInvoker)delegate
{
isFinished = false;
ReadFromFrame(bitmap);
isFinished = true;
});
}
private void ReadFromFrame(Bitmap bitmap)
{
UpdateRuntimeSettingsWithUISetting();
TextResult[] textResults = null;
int timeElapsed = 0;
try
{
DateTime beforeRead = DateTime.Now;
textResults = mBarcodeReader.DecodeBitmap(bitmap, "");
DateTime afterRead = DateTime.Now;
timeElapsed = (int)(afterRead - beforeRead).TotalMilliseconds;
if (textResults == null || textResults.Length <= 0)
{
return;
}
if (textResults != null)
{
mDSManager.StopCamera();
Bitmap tempBitmap = ((Bitmap)(bitmap)).Clone(new Rectangle(0, 0, bitmap.Width, bitmap.Height), bitmap.PixelFormat);
this.BeginInvoke(mPostShowFrameResults, tempBitmap, textResults, timeElapsed, null);
}
}
catch (Exception ex)
{
this.Invoke(mPostShowFrameResults, new object[] { bitmap, textResults, timeElapsed, ex });
}
}
改造完成。以下是摄像头扫码功能的运行效果:
源码
https://github.com/yushulx/dotnet-webcam-barcode-reader
来源:oschina
链接:https://my.oschina.net/yushulx/blog/3224785