Розпізнавання штрих кодів і QR в додатках UWP


Я не відкрию Америку, якщо скажу, що найпопулярнішою бібліотекою для розпізнавання штрих-коду є ZXing («Zebra Crossing»). Список підтримуваних форматів досить значний і включає в себе: EAN-8 та EAN-13, QR Code, UPC-A та UPC-E, Code 39, Code 93, Code 128 та інші.

Є порт і для WinRT, а значить, бібліотеку можна використовувати і з універсальною платформою Windows.

PCL для розпізнавання штрих-кодів називається ZXing.Net
Назва як би натякає, що можна використовувати цю бібліотеку ще й з додатками .Net

На що відразу довелося звернути увагу. Якщо робимо просту фото, то зазвичай не утруждаем себе і просто робимо знімок. Якщо ж ми обробляємо якийсь знімок, то необхідно, щоб якість картинки було найкращим. Тому при ініціалізації камери необхідно встановити максимально можливе вирішення. Для цього використовуємо код подібний на код прикладу: Camera resolution sample

Крім того, необхідно визначити присутній у пристрої фронтальна або задня камера. Це нескладно:

var devices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
DeviceInformation frontCamera = null;
DeviceInformation rearCamera = null;
foreach (var device in devices)
{
switch (device.EnclosureLocation.Panel)
{
case Windows.Devices.Enumeration.Panel.Front:
frontCamera = device;
break;
case Windows.Devices.Enumeration.Panel.Back:
rearCamera = device;
break;
}
}

Після чого можна ініціалізувати захоплення медіа:

if (rearCamera != null)
{
await mediaCapture.InitializeAsync(new MediaCaptureInitializationSettings { VideoDeviceId = rearCamera.Id });
}

Установка дозволу відбувається наступним чином:

public async Task SetResolution()
{
System.Collections.Generic.IReadOnlyList<IMediaEncodingProperties> res;
res = mediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.Photo);
uint maxResolution = 0;
int indexMaxResolution = 0;
if (res.Count >= 1)
{
for (int i = 0; i < res.Count; i++)
{
VideoEncodingProperties vp = (VideoEncodingProperties)res[i];
if (vp.Width > maxResolution)
{
indexMaxResolution = i;
maxResolution = vp.Width;
}
}
await mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.Photo, res[indexMaxResolution]);
}
}

Захоплення і розпізнавання зображення буде відбуватися приблизно так:

IRandomAccessStream fileStream = new InMemoryRandomAccessStream();
await mediaCapture.CapturePhotoToStreamAsync(Windows.Media.MediaProperties.ImageEncodingProperties.CreateBmp(), fileStream);
string res = await BarcodeDecoder.DecodeStreamToBarcode(fileStream);

Для розпізнавання потоку використовується клас BarcodeDecoder, який був узятий у Mike Taulty

Код класу прихована під спойлером
internal static class BarcodeDecoder
{
static BarcodeReader barcodeReader;

static BarcodeDecoder()
{
barcodeReader = new ZXing.BarcodeReader();
barcodeReader.Options.PureBarcode = false;
barcodeReader.Options.Hints.Add(DecodeHintType.TRY_HARDER, true);
barcodeReader.Options.PossibleFormats = new BarcodeFormat[] { BarcodeFormat.QR_CODE,BarcodeFormat.All_1D };
barcodeReader.Options.TryHarder = true;
barcodeReader.AutoRotate = true;
}

public async static Task<string> DecodeStreamToBarcode(IRandomAccessStream photoStream)
{
BitmapDecoder bitmapDecoder = await BitmapDecoder.CreateAsync(photoStream);
BitmapTransform emptyBitmapTransform = new BitmapTransform();

PixelDataProvider pixelDataProvider = await bitmapDecoder.GetPixelDataAsync(
BitmapPixelFormat.Rgba8,
BitmapAlphaMode.Premultiplied,
emptyBitmapTransform,
ExifOrientationMode.RespectExifOrientation,
ColorManagementMode.DoNotColorManage);

var zxingResult = barcodeReader.Decode(pixelDataProvider.DetachPixelData(),
(int)bitmapDecoder.PixelWidth, (int)bitmapDecoder.PixelHeight, BitmapFormat.RGBA32);

string res = "";
if (!String.IsNullOrEmpty(zxingResult?.Text)) res = zxingResult?.Text;
return res;
}
}


В даному класі розпізнаються штрих-коди форматів QR_CODE і All_1D. При цьому All_1D включає в себе наступні формати: UPC-A, UPC-E, EAN-8 та EAN-13. Можна додати ще і інші формати. Скажімо, декодер, який ми використовуємо, підтримує наступні формати: UPC-A, UPC-E, EAN-8, EAN-13, Code 39, Code 93, Code 128, ITF, Codabar, MSI, RSS-14 (всі варіанти), QR Code, Data Matrix, Aztec і PDF-417

Такий спосіб розпізнавання добре підходить для вже наявних зображень. Якщо ж використовувати камеру, то доведеться зробити безліч фотографій, поки не вийде вдалий розпізнаваний знімок. Іншими словами, перший млинець виявився глевким, але якийсь корисний досвід був отриманий.

Розпізнавання коду на льоту

В результаті інтенсивних пошуків був знайдений наступний репозиторій VideoScanZXingWinRT, який і був узятий за основу. Захоплення зображення відбувається з режиму превью.

Тут до мене трохи запізно прийшло усвідомлення того, що камера без можливостей фокусування не зможе зробити нормальний макрознімок. Теоретично, можна пробувати сканувати штрих-код і телефонами з камерою без автофокусу, але шанси малі. Можете спробувати самі. Камери ж з фокусуванням можуть підтримувати автофокусування, а можуть фокусуватися в ручному режимі. Для ручного режиму я зробив автофокусування по таймеру раз в 3 секунди.

Крім того була відключена підсвічування, яка створювала відблиски і заважала розпізнаванню

if (mediaCapture.VideoDeviceController.FlashControl.Supported) mediaCapture.VideoDeviceController.FlashControl.Auto = false;

В результаті видалення зайвого і внесення виправлень вийшов такий ось приклад:

Barcode_Scanner_UWP

Запрошую всіх приєднатися до проекту. Буду радий поліпшень і коригувань.

Для використання в своєму проекті необхідно додати нестандартний елемент управління BarcodeScannerControl.xaml разом з C# code behind. Потім в код сторінки, з якої буде відбуватися виклик (у прикладі це MainPage.xaml), додати Popup з User Control всередині

<Popup x:Name="BarcodePopup" IsOpen="False" IsLightDismissEnabled="False">
<Grid>
<local:BarcodeScannerControl x:Name="barcodecontrol" Width="{Binding ElementName=MainGrid,Path=ActualWidth}" 
Height="{Binding ElementName=MainGrid,Path=ActualHeight}"></local:BarcodeScannerControl>
</Grid>
</Popup>

В даному випадку ширина і висота користувача елементу управління прив'язані до розмірів елемента контейнера вікна з ім'ям MainGrid.

Код C# необхідно додати 2 методу. Один буде викликаний в разі успішного пошуку штрихкоду, другий у разі помилки. Сигнатури методів такі, що параметром одного є string, параметром другого exception. У прикладі це void BarcodeFound(string barcode) та void OnError(Exception e)
Тепер можна відкрити Popup і виконати метод, який запускає превю картинки з камери і сканування штрих-коду:

BarcodePopup.IsOpen = true;
await barcodecontrol.StartScan(BarcodeFound, OnError);

Один момент, про який не слід забувати – життєвий цикл програми UWP.
В нашому випадку, якщо додаток переходить в стан «призупинено», необхідно зупинити процес перегляду зображення з камери, пошуку штрих-коду і закрити Popup. Тобто додати ось такий код:

private async void App_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
await barcodecontrol.Cleanup();
BarcodePopup.IsOpen = false;
deferral.Complete();
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
Application.Current.Suspending += App_Suspending;
}

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
Application.Current.Suspending -= App_Suspending;
}

Тепер про мінуси. Мінус один, – бібліотека ZXing.Net не оновлювалася вже 2 роки. Хоча з іншого боку, якщо працює без багів і не потрібні нові функції, то, можливо, нічого страшного в цьому немає. Проект неофіційний, тому робота над ним триває коли у розробників з'являється вільний час. Сайт на олдскульном Codeplex цілком собі подає ознаки життя. На ньому ж можна знайти і вихідний код. Хороша новина, — оновлення самої бібліотеки планується в найближчому майбутньому, тобто проект не покинутий.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.