CommandLineMatrixScanCameraSample.c
MatrixScan Sample Without User Interface
This example uses the Scandit SDK to run MatrixScan (Tracking) using frames from a camera.
This demo is currently limited to GNU/Linux systems with a Video4Linux 2 camera.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <Scandit/ScRecognitionContext.h>
#include <Scandit/ScBarcodeScanner.h>
#include <Scandit/ScCamera.h>
#include <Scandit/ScObjectTracker.h>
#include <Scandit/ScTrackedObject.h>
// Please insert your license key here:
#define SCANDIT_SDK_LICENSE_KEY "-- INSERT YOUR LICENSE KEY HERE --"
// Please insert the desired default camera resolution here:
#define DEFAULT_RESOLUTION_WIDTH 1280
#define DEFAULT_RESOLUTION_HEIGHT 720
static void catch_exit(int signo) {
printf("SIGINT received.\n");
process_frames = SC_FALSE;
}
printf("This camera uses discrete resolutions:\n");
ScSize resolution_array[20];
ScFramerate framerate_array[10];
int32_t const resolution_count =
sc_camera_query_supported_resolutions(cam, &resolution_array[0], 20);
for (int32_t i = 0; i < resolution_count; i++) {
int32_t const framerate_count =
sc_camera_query_supported_framerates(cam, resolution_array[i], framerate_array, 10);
for (int32_t j = 0; j < framerate_count; j++) {
printf("\t%u:%u @ %.2f FPS\n",
resolution_array[i].width,
resolution_array[i].height,
fps);
}
}
}
// This callback gets emitted when a new object appears in the camera feed.
// Use this callback to start to draw a location.
printf("Barcode #%u: %s '%s' appeared.\n",
id,
data.str);
} else {
printf("Object #%u appeared.\n", id);
}
}
// This callback gets emitted when an existing object has been found
// in a new location.
if (sc_barcode_is_recognized(barcode)) {
ScByteArray data = sc_barcode_get_data(barcode);
printf("Barcode #%u: %s '%s' was updated.\n",
id,
sc_symbology_to_string(sc_barcode_get_symbology(barcode)),
data.str);
} else {
printf("Object #%u was updated.\n", id);
}
}
// This callback gets emitted when an object was no longer found.
// Use this callback to disable your drawing task.
// Be aware that it also gets triggered on objects that have not been recognized.
printf("Object #%u was lost.\n", tracking_id);
}
// Use this callback to update the drawing location of an object. Predictions
// are made even if the object was not found for a certain time.
}
int main(int argc, char const *argv[]) {
// Handle ctrl+c events.
if (signal(SIGINT, catch_exit) == SIG_ERR) {
printf("Could not set up signal handler.\n");
return -1;
}
// Create the camera object.
ScCamera *camera = NULL;
if (argc > 1) {
// Setup the camera from a device path. E.g. /dev/video1
// We use 4 image buffers.
camera = sc_camera_new_from_path(argv[1], 4);
} else {
// When no parameters are given, the camera is automatically detected.
camera = sc_camera_new();
}
if (camera == NULL) {
printf("No camera available.\n");
return -1;
}
uint32_t resolution_width = DEFAULT_RESOLUTION_WIDTH;
uint32_t resolution_height = DEFAULT_RESOLUTION_HEIGHT;
// Read the desired resolution form the command line.
if (argc == 4) {
resolution_width = atoi(argv[2]);
resolution_height = atoi(argv[3]);
}
// Get the supported resolutions and check
// if the desired resolution is supported
uint32_t const resolutions_size = 30;
ScSize resolutions[resolutions_size];
int32_t resolutions_found;
ScStepwiseResolution swres;
switch (resm) {
print_all_discrete_resolutions(camera);
// The camera supports a small set of predefined resolutions
resolutions_found = sc_camera_query_supported_resolutions(
camera, &resolutions[0], resolutions_size);
if (!resolutions_found) {
printf("There was an error getting the discrete resolution capabilities of the "
"camera.\n");
return -1;
}
for (int i = 0; i < resolutions_found; i++) {
if (resolutions[i].width == resolution_width &&
resolutions[i].height == resolution_height) {
supported = SC_TRUE;
break;
}
}
break;
// The camera supports a wide range of resolutions that are
// generated step-wise. Refer to documentation for further
// explanation.
printf("There was an error getting the stepwise resolution capabilities of the "
"camera.\n");
return -1;
}
printf("This camera uses step-wise resolutions:\n");
swres.min_height <= resolution_height && resolution_height <= swres.max_height &&
resolution_width % swres.step_width == 0 &&
resolution_height % swres.step_height == 0) {
supported = SC_TRUE;
}
break;
default:
printf("Could not get camera resolution mode.\n");
return -1;
}
// Set the resolution
if (!supported) {
printf("%dx%d is not supported by this camera.\nPlease specify a supported resolution on "
"the command line or in the source code.\n",
resolution_width,
resolution_height);
sc_camera_release(camera);
return -1;
}
ScSize desired_resolution;
desired_resolution.width = resolution_width;
desired_resolution.height = resolution_height;
printf("Setting resolution failed.\n");
sc_camera_release(camera);
return -1;
}
// Start streaming.
printf("Start the camera failed.\n");
sc_camera_release(camera);
return -1;
}
// 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
ScRecognitionContext *context =
if (context == NULL) {
printf("Could not initialize context.\n");
sc_camera_release(camera);
return -1;
}
// Create barcode scanner with EAN13/UPCA and QR code scanning enabled.
// The default preset is optimized for real-time frame processing using a
// camera.
ScBarcodeScannerSettings *settings =
if (settings == NULL) {
sc_recognition_context_release(context);
sc_camera_release(camera);
return -1;
}
// We want to track at most one so that the command line output remains readable.
// In a more realistic MatrixScan scenario this number should be set to the number of
// expected codes that have to be tracked at the same time.
// We disable looking at a default scan area to get a smother MatrixScan experience
// Our camera has no auto-focus.
// Codes are most likely oriented from left to right.
// Only keep codes for one frame and do not accumulate anything.
// Accumulating many codes over a long scan session can slow down the scanning speed
// significantly.
sc_barcode_scanner_settings_set_code_duplicate_filter(settings, 0);
sc_barcode_scanner_settings_set_code_caching_duration(settings, 0);
// Create a barcode scanner for our context and settings.
sc_barcode_scanner_settings_release(settings);
if (scanner == NULL) {
sc_recognition_context_release(context);
sc_camera_release(camera);
return -1;
}
// The scanner is setup asynchronous.
// We could wait here using sc_barcode_scanner_wait_for_setup_completed if needed.
// Setup the object tracker and it's callbacks used for MatrixScan.
ScObjectTrackerCallbacks callbacks = {on_appeared, on_updated, on_lost, on_predicted};
// We don't pass custom data to the callbacks in this simple example.
// The tracker is enabled by default.
// ... but it can be disabled on demand.
// sc_object_tracker_set_enabled(tracker, SC_FALSE);
// Signal a new frame sequence to the context.
// Create an image description that is reused for every frame.
process_frames = SC_TRUE;
while (process_frames) {
// Get the latest camera frame data and description
if (image_data == NULL) {
printf("Frame access failed. Exiting.\n");
break;
}
// Process the frame.
ScProcessFrameResult result =
sc_recognition_context_process_frame(context, image_descr, image_data);
printf("Processing frame failed with error %d: '%s'\n",
result.status,
}
// Signal the camera that we are done reading the image buffer.
sc_camera_enqueue_frame_data(camera, image_data);
}
// Signal to the context that the frame sequence is finished.
// Cleanup all objects.
sc_image_description_release(image_descr);
sc_object_tracker_release(tracker);
sc_barcode_scanner_release(scanner);
sc_recognition_context_release(context);
sc_camera_release(camera);
}