Can ZXing.NET read multiple QR barcodes in same image? - c#

I'm in the process of integrating ZXing.NET into a WPF application but I'm using the BarcodeReader.Decode method to read the QR barcode. It works but now I'm wondering what happens when the image have multiple QR barcodes. I want ZXing to read them all into an array. Is this possible? If yes, how? I don't see any method that does this.
Edit:
I found out there is a class called QRCodeMultiReader that I can use to read multiple QR barcodes. But the parameter that you send to the decodeMultiple method is of type ZXing.BinaryBitmap. How do I convert a Bitmap into a BinaryBitmap?
Here is what I have, but the results variable is always null when I call decodeMultiple:
public static List<BarCodeDataContract> MultipleDecode(Bitmap image)
{
var converter = new ImageConverter();
var bytes = (byte[])converter.ConvertTo(image, typeof(byte[]));
LuminanceSource source = new RGBLuminanceSource(bytes, image.Width, image.Height);
var binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
var reader = new QRCodeMultiReader();
var results = reader.decodeMultiple(binaryBitmap);
var scannedQrBarcodes = new List<BarCodeDataContract>();
foreach (var result in results)
{
if (result == null || result.BarcodeFormat != BarcodeFormat.QR_CODE || !result.Text.Contains(":")) return null;
var qrCodeData = result.Text.Split(':');
if (qrCodeData.Length != 2) return null;
int numericCode;
if (int.TryParse(qrCodeData[0], out numericCode))
{
if (System.Enum.IsDefined(typeof(QrCodeActionPrefixType), (short)numericCode))
{
Guid guid;
if (Guid.TryParse(qrCodeData[1], out guid))
{
scannedQrBarcodes.Add(new BarCodeDataContract()
{
QrCodeActionPrefixType = (QrCodeActionPrefixType)numericCode,
BarCodeObjectUniqueId = guid
});
}
}
}
}
return scannedQrBarcodes;
}

You can create a new BinaryBitmap by using a Binarizer that processes a LuminanceSource. These are all ZXing related classes.
To get the LuminanceSource you can create a new BitmapLuminanceSource that you pass your Bitmap. (BitmapLuminanceSource' base class is BaseLuminanceSource which base class is LuminanceSource, that is what you want)
The Binarizer turns your LuminanceSource into a BinaryBitmap. This means the resulting image will only have black and white pixels or dots (binary). There are multiple ways to binarize an image and some ways are more suited for other conditions, like a dark background for example. You can try out different Binarizers.
Code:
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(new BitmapLuminanceSource(_insert your Bitmap here_)));

The BarcodeReader class implements two interfaces: IBarcodeReader and IMultipleBarcodeReader.
The IMultipleBarcodeReader interface supports the method DecodeMultiple.
If you set the Property PossibleFormats only to QR_CODE the BarcodeReader uses internally the QRCodeMultiReader implementation if DecodeMultiple is called. If you look for other barcode types it uses the GenericMultipleBarcodeReader.
Btw. if the QR_CODEs are generated with Structured Append support you will find in the ResultMetadata collection the sequence number and parity information for every result which is found. That information helps to build up the final result in the correct order if needed.

Related

Send InkCanvas strokes directly to IRandomnaccessStream

I'm trying to get a byte array from the InkCanvas control, but the method i've come up with so far seems a bit long winded.
Currently I use the following:
StorageFolder folder = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFolderAsync("Temp");
StorageFile file = await folder.CreateFileAsync(GenerateString(5)+".zzx", Windows.Storage.CreationCollisionOption.GenerateUniqueName);
using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
await SignatureCanvas.InkPresenter.StrokeContainer.SaveAsync(stream);
var array = await IRandomAccessStreamToByteArray(stream);
}
The custom stream reader is as follows.
private async Task<byte[]> IRandomAccessStreamToByteArray(IRandomAccessStream stream)
{
var reader = new DataReader(stream.GetInputStreamAt(0));
var bytes = new byte[stream.Size];
await reader.LoadAsync((uint)stream.Size);
reader.ReadBytes(bytes);
return bytes;
}
This works, and gives me the byte array that i need, but also leaves me with unwanted images. Was having some access issues due to files still being written when another call wanted to replace the file so decided to go down the multiple images route. Is there a way to skip the image file entirely? It's not too much of an issue to clear out a temp folder, but if it can be avoided that would be preferable.
I had read somewhere already that InkCanvas doesn't support direct to array dumps, so any suggestions would be appreciated!
If you want to save your InkCanvas strokes to bytes Array without create the file, you should be able to use the Win2D.uwp.
To install Win2D.uwp, run the "Install-Package Win2D.uwp" command in the Package Manager Console.
There is a CanvasDrawingSession.DrawInk method to draw a collection of ink strokes. That we should be able to use CanvasBitmap.GetPixelBytes method to get an array of raw byte data for the entire bitmap.
For example:
private byte[] ConvertInkCanvasToByteArray()
{
var canvasStrokes = SignatureCanvas.InkPresenter.StrokeContainer.GetStrokes();
if (canvasStrokes.Count > 0)
{
var width = (int)SignatureCanvas.ActualWidth;
var height = (int)SignatureCanvas.ActualHeight;
var device = CanvasDevice.GetSharedDevice();
device.DeviceLost += DeviceOnDeviceLost;
var renderTarget = new CanvasRenderTarget(device, width, height, 96);
using (var ds = renderTarget.CreateDrawingSession())
{
ds.Clear(Windows.UI.Colors.White);
ds.DrawInk(SignatureCanvas.InkPresenter.StrokeContainer.GetStrokes());
}
return renderTarget.GetPixelBytes();
}
else
{
return null;
}
}
private void DeviceOnDeviceLost(CanvasDevice sender, object args)
{
Debug.WriteLine("DeviceOnDeviceLost");
}
Also if we want to convert the bytes array to image, we should be able to use following code:
WriteableBitmap bitmap = new WriteableBitmap((int)SignatureCanvas.ActualWidth, (int)SignatureCanvas.ActualHeight);
await bitmap.PixelBuffer.AsStream().WriteAsync(mybytes, 0, mybytes.Length);

Cannot convert type 'WriteableBitmap' to 'LuminanceSource' on UWP for barcode decoding

I'm trying to use ZXing on UWP project and I have read a lots of tutorials, that I can't get to work. The last tutorial said that I should use WritableBitmap, 'cos Bitmap is not awailable in UWP.
However it says to me
Cannot convert type 'Windows.UI.Xaml.Media.Imaging.WriteableBitmap' to
'ZXing.LuminanceSource'
public class QrCodeHelpers
{
public static void ReadQrCodeFromBitmap(WriteableBitmap image)
{
IBarcodeReader reader = new BarcodeReader();
var generic = new BarcodeReaderGeneric<WriteableBitmap>();
// detect and decode the barcode inside the bitmap
var result = reader.Decode((ZXing.LuminanceSource)image);
// do something with the result
}
}
How could I get this work? I have an image from MediaCapture and it would be fine to use that and get the QR code's data. Any solution?
First of all, I agree with Peter Duniho.
Then, if you want to use reader.Decode() method, the parameter is actually SoftwareBitmapLuminanceSource.
You can convert the WriteableBitmap to SoftwareBitmap at first and then convert it to SoftwareBitmapLuminanceSource. And in your code IBarcodeReader reader = new BarcodeReader();, is this a typo? IBarcodeReader is an interface.
Any way, you can code for example like this:
SoftwareBitmap sbmp = SoftwareBitmap.CreateCopyFromBuffer(wbmp.PixelBuffer,
BitmapPixelFormat.Bgra8,
wbmp.PixelWidth,
wbmp.PixelHeight); //converter WriteableBitmap to SoftwareBitmap, wbmp represents the WriteableBitmap
//convert SoftwareBitmap to SoftwareBitmapLuminanceSource
SoftwareBitmapLuminanceSource luminanceSource = new SoftwareBitmapLuminanceSource(sbmp);
BarcodeReader reader = new BarcodeReader(); //change IBarcodeReader to BarcodeReader
var generic = new BarcodeReaderGeneric<WriteableBitmap>(); //This code for what?
var result = reader.Decode(luminanceSource);

C# - save base64 string to image file without losing transparency

I am trying to write a method that takes image data in base64 string and saves it into a binary file while preserving transparency (for example in case of PNGs).
My other requirement is that this needs to be done in C# in PCL (Portable Class Library).
I know that you can use Image or WriteableBitmap to solve this issue but such classes are not available in PCL.
I have the following method that does the job of taking the base64 data and saving it to a file:
public static async Task Base64ToBinaryImageFile(IFile file, string base64Content)
{
var bytes = Convert.FromBase64String(base64Content);
using (var stream = await file.OpenAsync(FileAccess.ReadAndWrite))
{
stream.Seek(0, SeekOrigin.Begin);
using (var writer = new BinaryWriter(stream))
{
writer.Write(content);
writer.Flush();
}
}
}
It works fine except that:
I lose the transparency data (so transparent pixels show up as black).
The file that is created using this method has a larger size (in bytes) than the original file.
Any idea on what's the cause and how to fix these issues?
Update: Here is the JavaScript code that sends the base64 data to C#:
function onPaste(event) {
var $event = event.data.$;
var clipboardData = $event.clipboardData;
var found = false;
var imageType = /^image/;
if (!clipboardData) {
return false;
}
return Array.prototype.forEach.call(clipboardData.types, function (type, i) {
if (found) {
return false;
}
if (type.match(imageType) || clipboardData.items[i].type.match(imageType)) {
readImageAsBase64(clipboardData.items[i]);
return found = true;
}
return false;
});
}
function readImageAsBase64(item) {
if (!item || typeof item.getAsFile !== "function") {
return;
}
var file = item.getAsFile();
var reader = new FileReader();
reader.onload = function (evt) {
window.external.notify("pasteImageBase64/" + evt.target.result);
};
reader.readAsDataURL(file);
}
I foresee a few possible issues:
Your issue could reside in the base64Content provided by the caller. It's possible that the conversion to base64Content that is provided as input to your method is reading the PNG image with an incorrect file format.
Related to #1, it's possible that someone calling the method took a .JPG or .BMP image file, naively renamed it to .PNG extension and called your method assuming that they were sending a PNG image, when in fact they were not.
You may be opening the .PNG image in testing with an image viewer/editor that does not support transparency or handle it well (IE mspaint.exe)

Manipulate PDF objects

I am trying to manipulate a PDF, it functions as a template. What I am trying is replacing 'placeholders' in the PDF template with my data. So someone makes a PDF template in Scribus for example, and adds an empty image with the name "company_logo". My application sees an image placeholder with the name "company_logo" and it adds the company logo there.
I can browse AcroFields with iTextSharp library and set text in a text field (for example) but AcroFields doesn't list the image placeholder. I've got the feeling that AcroFields is not what I am looking for.
So how can I get a list (or tree) of all objects from the PDF and read their properties (like position, size, contents, etc).
P.S. I do not necessarily need to use iTextSharp, any other PDF lib will do as well. Preferably free.
A little pseudo code to make myself more clear
var object = Pdf.GetObjectById("company_logo");
object.SetValue(myImage);
object.SetPosition(x, y);
From your pseudo-code example, we understand that you want to replace the stream of an object that contains an image. There are several examples on how to do this.
For instance, in the SpecialID example, we create a PDF where we mark a specific image with a special ID. In the ResizeImage example, we track that image based on that special ID and we replace the stream:
object = reader.getPdfObject(i);
if (object == null || !object.isStream())
continue;
stream = (PRStream)object;
if (value.equals(stream.get(key))) {
PdfImageObject image = new PdfImageObject(stream);
BufferedImage bi = image.getBufferedImage();
if (bi == null) continue;
int width = (int)(bi.getWidth() * FACTOR);
int height = (int)(bi.getHeight() * FACTOR);
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
AffineTransform at = AffineTransform.getScaleInstance(FACTOR, FACTOR);
Graphics2D g = img.createGraphics();
g.drawRenderedImage(bi, at);
ByteArrayOutputStream imgBytes = new ByteArrayOutputStream();
ImageIO.write(img, "JPG", imgBytes);
stream.clear();
stream.setData(imgBytes.toByteArray(), false, PRStream.NO_COMPRESSION);
stream.put(PdfName.TYPE, PdfName.XOBJECT);
stream.put(PdfName.SUBTYPE, PdfName.IMAGE);
stream.put(key, value);
stream.put(PdfName.FILTER, PdfName.DCTDECODE);
stream.put(PdfName.WIDTH, new PdfNumber(width));
stream.put(PdfName.HEIGHT, new PdfNumber(height));
stream.put(PdfName.BITSPERCOMPONENT, new PdfNumber(8));
stream.put(PdfName.COLORSPACE, PdfName.DEVICERGB);
}
You will find another example in the book The Best iText Questions on StackOverflow where I answered the following question: PDF Convert to Black And White PNGs
I wrote the ReplaceImage example to show how to replace the image:
public static void replaceStream(PRStream orig, PdfStream stream) throws IOException {
orig.clear();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
stream.writeContent(baos);
orig.setData(baos.toByteArray(), false);
for (PdfName name : stream.getKeys()) {
orig.put(name, stream.get(name));
}
}
As you can see, it isn't as trivial as saying:
var object = Pdf.GetObjectById("company_logo");
object.SetValue(myImage);
As I explained in my comment, this doesn't make sense:
object.SetPosition(x, y);
The objects we're manipulating are streams that are used as Image XObjects. The advantage of having Image XObjects is that they can be reused. For instance: if you have the same logo on every page, then you want to store the bytes of that image only once and reuse the same logo multiple times. This means that the object with the image bytes doesn't know anything about its position. The position is determined in the content stream. It depends on the CTM.
Did you have a look at the scribus scripting capabilities?
Since you create a tamplate in scribus You could also write a short python script which replaces your placeholders with your final data and exports the final PDF.
Since scribus 1.5 it is also possible to call the python scripts from the commandline.

How to use the Nokia Imaging SDK's BlendFilter on WP8?

I'm staring with the Nokia Imaging SDK to play a little with it. Now, I'm facing the problem where I have an Image which already exists (in a folder in my visual studio solution) and I want to convert this image in order to use it in the BlendFilter class of the Nokia Imaging SDK. However I don't know how to use it.
I was trying to convert the existing image in a stream and then pass it as a parameter to the BlendFilter constructor. But not luck. The compiler says that the best overload method match ... has some invalid arguments.
This is the way I'm trying to load the existing image to a stream:
Image image = new Image();
image.Source = new BitmapImage(new Uri("/Images/Template3.2.png", UriKind.Relative));
BitmapImage bitImage = new BitmapImage(new Uri("/Images/Template3.2.png", UriKind.Relative));
WriteableBitmap Bitmap = new WriteableBitmap(bitImage);
And then:
var BlendFilter = new BlendFilter(bitImage, BlendFunction.Add); --> the compiler error is here
Does anyone know how to use the BlendFilter class? any example would be very helpful.
Regards!
Blend filter takes an IImageProvider as input. That means you can use any of the X-ImageSource classes as input and it will do all the work internally.
If you have a stream of the image I suggest you create an StreamImageSource and pass that to BlendFilter.
The list of different image sources is quite long, I suggest you look into the documentation and chose the one that is most appropriate to you.
Here is an example that takes a stream of an image as input, and blends a new image on top of it. For simplicity the other image is just an image filled with one color (ColorImageSource), but you can set any IImageProvider as source: chose the most convenient one.
using (var backgroundSource = new StreamImageSource(stream))
using (var filterEffect = new FilterEffect(backgroundSource))
{
using (BlendFilter blendFilter = new BlendFilter())
{
var size = new Windows.Foundation.Size(400, 400);
var color = Windows.UI.Color.FromArgb(250, 128, 255, 200);
blendFilter.ForegroundSource = new ColorImageSource(size, color);
blendFilter.BlendFunction = BlendFunction.Add;
filterEffect.Filters = new[] { blendFilter };
var result = await new JpegRenderer(filterEffect).RenderAsync();
}
}

Categories

Resources