Detecting 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 <assert.h>
#include <wand/magick-wand.h>
// Please insert your app key here:
static char const * const ENABLED_FILE_EXTENSIONS[] = {
typedef struct InputImage {
char const *file_name;
struct InputImage const *next;
} InputImage;
static int has_valid_extension(char const *file_name)
char const * const *extension = ENABLED_FILE_EXTENSIONS;
for (; *extension != NULL; ++extension) {
const size_t file_name_length = strlen(file_name);
const size_t extension_length = strlen(*extension);
char const * const file_extension =
file_name + (file_name_length - extension_length);
if (file_name_length >= extension_length &&
strcmp(file_extension, *extension) == 0) {
return SC_TRUE;
return SC_FALSE;
static InputImage *push_if_valid_image(char const * filename,
InputImage *container) {
if (has_valid_extension(filename) == SC_TRUE) {
InputImage *new_image = malloc(sizeof(InputImage));
const size_t target_size = strlen(filename) + 1;
new_image->file_name = malloc(target_size);
strncpy((char*)new_image->file_name, filename, target_size);
new_image->next = container;
container = new_image;
return container;
InputImage const *get_input_files(int argc, char const *argv[]) {
InputImage *ret = NULL;
// We skip fist argument
for (int arg_idx = 1; arg_idx < argc; ++arg_idx) {
char const * const current_arg = argv[arg_idx];
DIR *dir;
struct dirent *ent;
if ((dir = opendir(current_arg)) != NULL) {
// We have a directory
while ((ent = readdir (dir)) != NULL) {
char *combined_name = malloc(sizeof(current_arg) +
sizeof(ent->d_name) + 2);
combined_name[0] = '\0';
strcat(combined_name, current_arg);
strcat(combined_name, "/");
strcat(combined_name, ent->d_name);
ret = push_if_valid_image(combined_name, ret);
closedir (dir);
} else {
// We have a file
ret = push_if_valid_image(current_arg, ret);
return ret;
static ScBool load_image(const char* image_name, uint8_t** data,
uint64_t* width, uint64_t *height) {
MagickWand *image = NULL;
image = NewMagickWand();
if (MagickReadImage(image, image_name) == MagickFalse) {
return SC_FALSE;
// Convert image to RGB, so we can access the pixel data directly.
assert(MagickSetImageFormat(image, "RGB"));
assert(MagickSetImageColorspace(image, RGBColorspace));
assert(MagickSetImageType(image, TrueColorType));
assert(MagickSetImageDepth(image, 8));
*width = MagickGetImageWidth(image);
*height = MagickGetImageHeight(image);
size_t blob_size = 0;
uint8_t* image_data = MagickGetImageBlob(image, &blob_size);
printf("image size: %dx%d (%d bytes)\n", (int)*width, (int)*height,
*data = malloc(blob_size);
memcpy(*data, image_data, blob_size);
return SC_TRUE;
int main(int argc, const char *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 should be written to /tmp. You can use any other writable data
// directory in your application.
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 default settings object 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 to false to only look for horizontal and centered codes.
const ScBool use_full_image_localization = SC_TRUE;
if (use_full_image_localization == SC_TRUE) {
// By setting the code location constraints to hint and code direction to none, we tell
// the engine to run the full-image localization for every frame.
} else {
// Turn off full-image localization and only run scanline along the
// direction of the code direction hint for every frame.
// 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.
uint64_t image_width;
uint64_t image_height;
if (load_image(current_image->file_name, &image_data, &image_width,
&image_height) == SC_FALSE) {
printf("Failed to load image '%s'.\n", current_image->file_name);
return -1;
// Fill the image description for our loaded image.
const uint32_t image_memory_size = image_width * image_height * 3;
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
printf("Processing frame failed with error %d: '%s'\n", result.status,
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.
ScBarcodeArray * new_codes =
uint32_t num_codes = sc_barcode_array_get_size(new_codes);
if (num_codes == 0) {
printf("no barcodes or QR codes found\n");
for (uint32_t i = 0; i < num_codes; ++i) {
const ScBarcode * barcode = sc_barcode_array_get_item_at(new_codes, i);
ScSymbology symbology = sc_barcode_get_symbology(barcode);
const char *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;