Decoding Barcodes in Images

This example uses the Scandit SDK to detect barcodes in images

It illustrates the following aspects:

  • Loading of images from disk using the ImageMagick library.
  • Setting up and initializing a barcode scanner to scan EAN13, UPCA and QR codes.
  • Handling of the recognized barcode results.
  • Closing of the recognition context and barcode scanner.
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_surface.h>
// Please insert your license key here:
static char const *const ENABLED_FILE_EXTENSIONS[] = {"png", "jpg", "jpeg", "tif", "bmp", NULL};
typedef struct InputImage {
char const *file_name;
struct InputImage const *next;
} InputImage;
static int has_valid_extension(char const *file_name, size_t file_name_length) {
char const *const *extension = ENABLED_FILE_EXTENSIONS;
for (; *extension != NULL; ++extension) {
size_t const extension_length = strlen(*extension);
if (file_name_length < extension_length) {
char const *const file_extension = file_name + (file_name_length - extension_length);
if (memcmp(file_extension, *extension, extension_length + 1) == 0) {
return SC_TRUE;
return SC_FALSE;
static InputImage *push_if_valid_image(char const *filename,
size_t filename_length,
InputImage *container) {
if (has_valid_extension(filename, filename_length) == SC_TRUE) {
InputImage *new_image = malloc(sizeof(InputImage));
char *new_filename = malloc(filename_length + 1);
memcpy(new_filename, filename, filename_length);
new_filename[filename_length] = '\0';
new_image->file_name = new_filename;
new_image->next = container;
container = new_image;
return container;
InputImage const *get_input_files(int argc, char const *argv[]) {
InputImage *ret = NULL;
// We skip the first argument
for (int arg_idx = 1; arg_idx < argc; ++arg_idx) {
char const *const current_arg = argv[arg_idx];
size_t const current_arg_length = strlen(current_arg);
DIR *dir;
struct dirent *ent;
if ((dir = opendir(current_arg)) != NULL) {
// We have a directory
while ((ent = readdir(dir)) != NULL) {
size_t const dir_length = strlen(ent->d_name);
size_t const combined_name_length = current_arg_length + dir_length + 1;
char *combined_name = malloc(combined_name_length + 1);
memcpy(combined_name, current_arg, current_arg_length);
combined_name[current_arg_length] = '/';
memcpy(combined_name + current_arg_length + 1, ent->d_name, dir_length);
combined_name[combined_name_length] = '\0';
ret = push_if_valid_image(combined_name, combined_name_length, ret);
} else {
// We have a file
ret = push_if_valid_image(current_arg, current_arg_length, ret);
return ret;
static ScBool load_image(char const *image_name,
uint8_t **data,
uint32_t *width,
uint32_t *height,
uint32_t *row_stride) {
SDL_Surface *image = IMG_Load(image_name);
if (image == NULL) {
printf("IMG_Load '%s' failed: %s\n", image_name, IMG_GetError());
return SC_FALSE;
SDL_Surface *image_rgb = SDL_ConvertSurfaceFormat(image, SDL_PIXELFORMAT_RGB24, 0);
if (image_rgb == NULL) {
printf("Image '%s' convertion failed: %s\n", image_name, IMG_GetError());
return SC_FALSE;
*width = image_rgb->w;
*height = image_rgb->h;
*row_stride = image_rgb->pitch;
int const blob_size = *row_stride * *height;
printf("Image '%s' size: %ux%u, stride %u (%u bytes)\n",
*data = malloc(blob_size);
memcpy(*data, image_rgb->pixels, blob_size);
return SC_TRUE;
int main(int argc, char const *argv[]) {
if (argc < 2) {
printf("Please provide paths to image files or directories as arguments.\n");
return -1;
printf("Scandit SDK Version: %s\n", SC_VERSION_STRING);
int return_code = 0;
ScRecognitionContext *context = NULL;
ScBarcodeScanner *scanner = NULL;
ScImageDescription *image_descr = NULL;
ScBarcodeScannerSettings *settings = NULL;
uint8_t *image_data = NULL;
InputImage const *const images = get_input_files(argc, argv);
// Create a recognition context. Files created by the recognition context and the
// attached scanners will be written to this directory. In production environment,
// it should be replaced with writable path which does not get removed between reboots
context = sc_recognition_context_new(SCANDIT_SDK_LICENSE_KEY, "/tmp", NULL);
if (context == NULL) {
printf("Could not initialize context.\n");
return_code = -1;
goto cleanup;
image_descr = sc_image_description_new();
if (image_descr == NULL) {
printf("Could not initialize image description.\n");
return_code = -1;
goto cleanup;
// The barcode scanner is configured by setting the appropriate properties on an
// "barcode scanner settings" instance. This settings object is passed to the barcode
// scanner when it is constructed. We start with the settings preset for single frame processing
// and enable only the symbologies we need. For the purpose of this demo, we would like to scan
// EAN13/UPCA and QR codes
if (settings == NULL) {
printf("Could not initialize settings.\n");
return_code = -1;
goto cleanup;
// Set the symbol count for CODE 128 symbology to be in range from 4 to 20.
uint16_t const range_from = 4;
uint16_t const range_to = 20;
uint16_t sym_count[16];
for (int i = range_from; i < range_to; ++i) {
sym_count[i - range_from] = i;
// Set the maximum number of codes to look for in an image, 1 in our case.
// By setting the code location constraints to ignore, we tell
// the barcode scanner to search for codes in the whole image in every frame.
// We make no assumptions about the most likely orientation of the codes.
// The barcode scanner allows to prevent codes from getting scanned again in
// a certain time interval (e.g., 500ms). The default setting is 0 what
// effectively disables this duplicate filtering.
// sc_barcode_scanner_settings_set_code_duplicate_filter(settings, 500);
// Create a barcode scanner for our context and settings.
scanner = sc_barcode_scanner_new_with_settings(context, settings);
if (scanner == NULL) {
printf("Could not initialize scanner.\n");
return_code = -1;
goto cleanup;
// Wait for the initialization of the barcode scanner. We could omit this call
// and start scanning immediately, but there is no guarantee that the barcode scanner
// operates at full capacity.
printf("barcode scanner setup failed.\n");
return_code = -1;
goto cleanup;
for (InputImage const *current_image = images; current_image != NULL;
current_image = current_image->next) {
// Load the image from disc.
uint32_t image_width, image_height, row_stride;
if (load_image(current_image->file_name,
&row_stride) == SC_FALSE) {
printf("Failed to load image '%s'.\n", current_image->file_name);
return_code = -1;
goto cleanup;
// Fill the image description for our loaded image.
uint32_t const image_memory_size = row_stride * image_height;
sc_image_description_set_width(image_descr, image_width);
sc_image_description_set_height(image_descr, image_height);
sc_image_description_set_memory_size(image_descr, image_memory_size);
// Signal to the context that a new sequence of frames starts. This call is mandatory,
// even if we are only going to process one image. Scanning will fail with
sc_recognition_context_process_frame(context, image_descr, image_data);
printf("Processing frame failed with error %d: '%s'\n",
return_code = -1;
goto cleanup;
// Signal to the context that the frame sequence is finished.
// Retrieve the barcode scanner object to get the list of codes that were recognized in
// the last frame.
// Get the list of codes that have been found in the last process frame call.
uint32_t num_codes = sc_barcode_array_get_size(new_codes);
if (num_codes == 0) {
printf("no 1d or 2d barcodes found\n");
for (uint32_t i = 0; i < num_codes; ++i) {
ScBarcode const *barcode = sc_barcode_array_get_item_at(new_codes, i);
ScSymbology symbology = sc_barcode_get_symbology(barcode);
char const *symbology_name = sc_symbology_to_string(symbology);
// For simplicity it is assumed that the barcode contains textual data, even
// though it is possible to encode binary data in QR codes that contain null-
// bytes at any position. For applications expecting binary data, use
// sc_byte_array_get_data_size() to determine the length of the returned data.
printf("barcode: symbology=%s, data='%s'\n", symbology_name, data.str);
image_data = NULL;
// Cleanup allocated data and objects. These functions all check for null values,
// so it's save to pass in null objects.
for (InputImage const *current_image = images; current_image != NULL;) {
InputImage const *next_image = current_image->next;
free((char *)current_image->file_name);
free((char *)current_image);
current_image = next_image;
return return_code;