为什么这不起作用? ZXing、位图、ImageSource
Why isnt this working? ZXing, Bitmap, ImageSource
我正在尝试从 WPF 中的相机扫描二维码(使用 Zxing - ZXing 的解码方法仅适用于位图)。这是我拥有的:
void Window2_Loaded(object sender, RoutedEventArgs e)
{
System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 5, 0);
LoaclWebCamsCollection = new FilterInfoCollection(FilterCategory.VideoInputDevice);
LocalWebCam = new VideoCaptureDevice(LoaclWebCamsCollection[1].MonikerString);
LocalWebCam.NewFrame += new NewFrameEventHandler(Cam_NewFrame);
LocalWebCam.Start();
dispatcherTimer.Start();
}
转换方法(位图源到位图)- 从另一个线程获取:
Bitmap GetBitmap(BitmapSource source)
{
Bitmap bmp = new Bitmap(
source.PixelWidth,
source.PixelHeight,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
BitmapData data = bmp.LockBits(
new System.Drawing.Rectangle(System.Drawing.Point.Empty, bmp.Size),
ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
source.CopyPixels(
Int32Rect.Empty,
data.Scan0,
data.Height * data.Stride,
data.Stride);
bmp.UnlockBits(data);
return bmp;
}
和定时器方法:
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
BarcodeReader Reader = new BarcodeReader();
if (frameHolder.Source != null)
{
Result result = Reader.Decode(GetBitmap((BitmapSource)frameHolder.Source));
decoded = result.ToString().Trim();
hey.Text = decoded;
}
}
和新的框架方法:
void Cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
System.Drawing.Bitmap img = (Bitmap)eventArgs.Frame.Clone();
MemoryStream ms = new MemoryStream();
img.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
bi.Freeze();
Dispatcher.BeginInvoke(new ThreadStart(delegate
{
frameHolder.Source = bi;
}));
}
catch (Exception ex)
{
}
}
frameHolder
is my Image Control.
我做错了什么?。我很确定他不能从 frameHolder
中得到 Bitmap
但我不知道为什么。
您需要将二维码设置为可能的格式,否则它不会搜索任何内容。
下面的代码应该可以帮助您传递图像路径:
public static string Find(string fileName)
{
if (File.Exists(fileName)) {
using (var bitmap = (Bitmap)Image.FromFile(fileName))
{
return Decode(bitmap, false, new List<BarcodeFormat> {BarcodeFormat.QR_CODE});
}
}
return null;
}
并解码:
private static string Decode(Bitmap bitmap, bool tryMultipleBarcodes, IList<BarcodeFormat> possibleFormats)
{
BarcodeReader barcodeReader = new BarcodeReader();
var previousFormats = barcodeReader.Options.PossibleFormats;
if (possibleFormats != null)
barcodeReader.Options.PossibleFormats = possibleFormats;
barcodeReader.Options.TryHarder = true;
barcodeReader.TryInverted = true;
barcodeReader.AutoRotate = true;
var result = barcodeReader.Decode(bitmap);
if (result != null) {
return result.ToString();
} else {
return null;
}
}
首先
你知道这个方法有效吗?您是否使用从原始 Cam_NewFrame
方法保存的位图对其进行了测试。
为了您的理智,这是您应该做的第一件事。例如
void Cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
...
img.Save("some path");
然后用它测试BarcodeReader
。
其次
为什么要麻烦地转换位图,然后再转换回来?
克隆位图时只需复制位图即可。然后在您的计时器方法中使用该克隆(显然使线程安全并正确处理它们)。
最后,阅读关于 BarcodeReader
的文档,您显然缺少很多配置(根据 Matt Beldons)回答
ZXing.Net 提供了一个特殊版本的 BarcodeReader class,它使用 BitmapSource 实例。
通常,如果您通过 nuget 将 ZXing.Net 包添加到您的项目中,应该会有对 zxing.presentation.dll 的引用。如果不是从二进制分发版手动添加它。
特殊的 BarcodeReader 位于命名空间 ZXing.Presentation 中。
您的方法的以下修改版本应该有效:
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
BarcodeReader Reader = new ZXing.Presentation.BarcodeReader();
if (frameHolder.Source != null)
{
Result result = Reader.Decode((BitmapSource)frameHolder.Source);
decoded = result?.Text;
hey.Text = decoded;
}
}
不要忘记检查 "result == null",如果没有找到条形码,就会发生这种情况。
顺便说一句,忘掉你的 DispatcherTimer 解决方案,把所有的工作都放在 Cam_NewFrame 里。
void Cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
System.Drawing.Bitmap img = (Bitmap)eventArgs.Frame.Clone();
// TODO: add some kind of mutex or similar here so that you don't start a new Decode before the previous one is finished
Dispatcher.BeginInvoke(new ThreadStart(delegate
{
// use the original BarcodeReader because we are using the bitmap instance directly
BarcodeReader Reader = new ZXing.BarcodeReader();
Result result = Reader.Decode(img);
decoded = result?.Text;
hey.Text = decoded;
}));
MemoryStream ms = new MemoryStream();
img.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
bi.Freeze();
Dispatcher.BeginInvoke(new ThreadStart(delegate
{
frameHolder.Source = bi;
}));
}
catch (Exception ex)
{
}
}
扔掉以下
...
System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 5, 0);
...
dispatcherTimer.Start();
...
Bitmap GetBitmap(BitmapSource source)
{
...
}
...
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
...
}
...
我没有编译或测试代码。把它当作正确方向的提示。
我正在尝试从 WPF 中的相机扫描二维码(使用 Zxing - ZXing 的解码方法仅适用于位图)。这是我拥有的:
void Window2_Loaded(object sender, RoutedEventArgs e)
{
System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 5, 0);
LoaclWebCamsCollection = new FilterInfoCollection(FilterCategory.VideoInputDevice);
LocalWebCam = new VideoCaptureDevice(LoaclWebCamsCollection[1].MonikerString);
LocalWebCam.NewFrame += new NewFrameEventHandler(Cam_NewFrame);
LocalWebCam.Start();
dispatcherTimer.Start();
}
转换方法(位图源到位图)- 从另一个线程获取:
Bitmap GetBitmap(BitmapSource source)
{
Bitmap bmp = new Bitmap(
source.PixelWidth,
source.PixelHeight,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
BitmapData data = bmp.LockBits(
new System.Drawing.Rectangle(System.Drawing.Point.Empty, bmp.Size),
ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
source.CopyPixels(
Int32Rect.Empty,
data.Scan0,
data.Height * data.Stride,
data.Stride);
bmp.UnlockBits(data);
return bmp;
}
和定时器方法:
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
BarcodeReader Reader = new BarcodeReader();
if (frameHolder.Source != null)
{
Result result = Reader.Decode(GetBitmap((BitmapSource)frameHolder.Source));
decoded = result.ToString().Trim();
hey.Text = decoded;
}
}
和新的框架方法:
void Cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
System.Drawing.Bitmap img = (Bitmap)eventArgs.Frame.Clone();
MemoryStream ms = new MemoryStream();
img.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
bi.Freeze();
Dispatcher.BeginInvoke(new ThreadStart(delegate
{
frameHolder.Source = bi;
}));
}
catch (Exception ex)
{
}
}
frameHolder
is my Image Control.
我做错了什么?。我很确定他不能从 frameHolder
中得到 Bitmap
但我不知道为什么。
您需要将二维码设置为可能的格式,否则它不会搜索任何内容。
下面的代码应该可以帮助您传递图像路径:
public static string Find(string fileName)
{
if (File.Exists(fileName)) {
using (var bitmap = (Bitmap)Image.FromFile(fileName))
{
return Decode(bitmap, false, new List<BarcodeFormat> {BarcodeFormat.QR_CODE});
}
}
return null;
}
并解码:
private static string Decode(Bitmap bitmap, bool tryMultipleBarcodes, IList<BarcodeFormat> possibleFormats)
{
BarcodeReader barcodeReader = new BarcodeReader();
var previousFormats = barcodeReader.Options.PossibleFormats;
if (possibleFormats != null)
barcodeReader.Options.PossibleFormats = possibleFormats;
barcodeReader.Options.TryHarder = true;
barcodeReader.TryInverted = true;
barcodeReader.AutoRotate = true;
var result = barcodeReader.Decode(bitmap);
if (result != null) {
return result.ToString();
} else {
return null;
}
}
首先
你知道这个方法有效吗?您是否使用从原始 Cam_NewFrame
方法保存的位图对其进行了测试。
为了您的理智,这是您应该做的第一件事。例如
void Cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
...
img.Save("some path");
然后用它测试BarcodeReader
。
其次
为什么要麻烦地转换位图,然后再转换回来?
克隆位图时只需复制位图即可。然后在您的计时器方法中使用该克隆(显然使线程安全并正确处理它们)。
最后,阅读关于 BarcodeReader
的文档,您显然缺少很多配置(根据 Matt Beldons)回答
ZXing.Net 提供了一个特殊版本的 BarcodeReader class,它使用 BitmapSource 实例。 通常,如果您通过 nuget 将 ZXing.Net 包添加到您的项目中,应该会有对 zxing.presentation.dll 的引用。如果不是从二进制分发版手动添加它。 特殊的 BarcodeReader 位于命名空间 ZXing.Presentation 中。 您的方法的以下修改版本应该有效:
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
BarcodeReader Reader = new ZXing.Presentation.BarcodeReader();
if (frameHolder.Source != null)
{
Result result = Reader.Decode((BitmapSource)frameHolder.Source);
decoded = result?.Text;
hey.Text = decoded;
}
}
不要忘记检查 "result == null",如果没有找到条形码,就会发生这种情况。
顺便说一句,忘掉你的 DispatcherTimer 解决方案,把所有的工作都放在 Cam_NewFrame 里。
void Cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
System.Drawing.Bitmap img = (Bitmap)eventArgs.Frame.Clone();
// TODO: add some kind of mutex or similar here so that you don't start a new Decode before the previous one is finished
Dispatcher.BeginInvoke(new ThreadStart(delegate
{
// use the original BarcodeReader because we are using the bitmap instance directly
BarcodeReader Reader = new ZXing.BarcodeReader();
Result result = Reader.Decode(img);
decoded = result?.Text;
hey.Text = decoded;
}));
MemoryStream ms = new MemoryStream();
img.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
bi.Freeze();
Dispatcher.BeginInvoke(new ThreadStart(delegate
{
frameHolder.Source = bi;
}));
}
catch (Exception ex)
{
}
}
扔掉以下
...
System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 5, 0);
...
dispatcherTimer.Start();
...
Bitmap GetBitmap(BitmapSource source)
{
...
}
...
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
...
}
...
我没有编译或测试代码。把它当作正确方向的提示。