Redth / ZXing.Net.Mobile

Barcode Scanner for Xamarin.iOS, Xamarin.Android, UWP and Tizen
MIT License
1.07k stars 700 forks source link

How to decode an image picked from a photo library? #981

Open meJevin opened 3 years ago

meJevin commented 3 years ago

Using Xamarin.Essentials I let the user pick a photo and try to decode its bytes like so:

            var selectedPhoto = await MediaPicker.PickPhotoAsync(new MediaPickerOptions()
            {
                Title = "Pick a photo to decode",
            });

            if (selectedPhoto == null)
            {
                return;
            }

            using var photoStream = await selectedPhoto.OpenReadAsync();
            using var ms = new MemoryStream();
            await photoStream.CopyToAsync(ms);

            var photoBytes = ms.ToArray();

            BarcodeReader reader = new BarcodeReader();
            reader.Decode(photoBytes);

But I'm getting a null error when hitting the Decode line.

How would one implement the functionality I'm going for?

SylvainMoingeon commented 3 years ago

Hello @meJevin, Did you find a way to decode from an image file ?

meJevin commented 3 years ago

@SylvainMoingeon, yes.

My solution was the following:

  1. I created an interface to decode an image given a file path
  2. I implemented that interface on iOS and Android

I am now picking a photo and getting its FullPath, which I pass into my aforementioned service.

In iOS and Android implementations it's easy to get pixel data of images when you have a file path. So I just convert the pixel data from the selected photo into a LuminanceSource and decode it using a BarcodeReader

SylvainMoingeon commented 3 years ago

If this can help, here is iOS and Android implementation with latest version (3.1.0-beta2) Maybe @meJevin has a better implementation to provide ?

On forms project :

var selectedPhoto = await Xamarin.Essentials.MediaPicker.PickPhotoAsync();
if (selectedPhoto == null)
 return;

string decodedText = DependencyService.Get<IZXingService>().Decode(selectedPhoto.FullPath);

IZXingService :

public interface IZXingService
{
    string Decode(string filePath);
}

On iOS :

public string Decode(string filePath)
{
    var reader = new UIImageBarcodeReader();
    var image = UIImage.FromFile(filePath);

    var result = reader.Decode(image);

    if (result != null)
    {
        return result.Text;
    }
    else
    {
        // handle as you want
    }
}

On Android :

public string Decode(string filePath)
{
    Bitmap bitmap = BitmapFactory.DecodeFile(filePath);
    byte[] rgbBytes = GetRgbBytes(bitmap);

    var bin = new HybridBinarizer(new RGBLuminanceSource(rgbBytes, bitmap.Width, bitmap.Height));
    var i = new BinaryBitmap(bin);
    var reader = new MultiFormatReader();
    var result = reader.decode(i);

    if (result != null)
    {
        return result.Text;
    }
    else
    {
        // handle as you want
    }
}

private static byte[] GetRgbBytes(Bitmap image)
{
    var rgbBytes = new List<byte>();
    for (int y = 0; y < image.Height; y++)
    {
        for (int x = 0; x < image.Width; x++)
        {
            var c = new Android.Graphics.Color(image.GetPixel(x, y));
            rgbBytes.AddRange(new[] { c.R, c.G, c.B });
        }
    }
    return rgbBytes.ToArray();
}
meJevin commented 3 years ago

@SylvainMoingeon my solution is pretty much the same :)

nchrisr commented 2 years ago

@meJevin @SylvainMoingeon Dropping this here for anyone who needs a UWP solution

public async Task<string> DecodeAsync(string filePath)
{
 
    var file = await StorageFile.GetFileFromPathAsync(filePath);
 

    //QR code conversion from jpeg and return string.

    SoftwareBitmap softwareBitmap;

    using (IRandomAccessStream  fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
    {
        
        BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);

        softwareBitmap =  await decoder.GetSoftwareBitmapAsync();

    }
    
    

    // create a barcode reader instance

    var barcodeReader = new BarcodeReader
 {
 AutoRotate = true,
 Options = { TryHarder = true }
 };

    
Result result = barcodeReader.Decode(softwareBitmap);


    // do something with the result
 
    if (result != null)
    {
 
        return result.Text;

    }
    
    return null;

}