EyeQ Docs
Getting started

C/C++ Quickstart

Getting started with Perfectly Clear SDK in C/C++

This guide walks you through correcting your first image using the Perfectly Clear SDK. This is a very simple example - it AI Scene Detection with the default Universal model. See the Sample projects section later in this guide for more complex examples, including using single presets, re-processing the same image with different correction parameters, and different I/O methods.

Project structure

Set up your project directory with the SDK extracted alongside your source files. Your project structure should look like this:

my-project/
├── main.cpp
├── Makefile
├── sample.jpg
└── perfectly-clear-sdk/
    └── Linux/
        ├── header/
        │   ├── PerfectlyClearPro.h
        │   └── PFCImageFile.h
        ├── bin/
        │   ├── (all necessary .pnn model files)        
        │   ├── libPerfectlyClearPro.a
        │   ├── libPFCImageFile.a
        │   ├── libwebp.a
        │   ├── liblcms2.so.2
        │   └── libexiv2.so.26
        └── sdk_license/
            └── (license files)

Activate the SDK license

Before processing images, activate your SDK license using PFC_SetProtectionPath(). Point it to the directory containing your license files.

#include "PerfectlyClearPro.h"

int licenseStatus = PFC_SetProtectionPath("./perfectly-clear-sdk/sdk_license");
if (licenseStatus == 0 || licenseStatus == 102 || licenseStatus == 103) {
    printf("SDK license activated successfully\n");
} else {
    printf("License activation failed (status: %d)\n", licenseStatus);
    return 1;
}

Status codes 0, 102 (active license), and 103 (active trial) indicate success. Any other code means that activation failed.

When you're done, call PFC_ReleaseProtectionPath() to release license resources.

Load an image

Use PFCImageFile to load an image from disk, then populate a PFCIMAGE structure for processing.

#include "PFCImageFile.h"

PFCImageFile imageFile;
if (imageFile.LoadImageFile("input.jpg", true, NULL) != LOAD_OK) {
    printf("Failed to load image\n");
    return 1;
}

PFCIMAGE image = {
    imageFile.width,
    imageFile.height,
    imageFile.stride,
    (PFCPIXELFORMAT)imageFile.pfcImageFormat(),
    imageFile.raw_image
};

// rotate image according to EXIF orientation tag
PFC_TransposeExif(&image, imageFile.exifOrientation()); 

The second parameter (true) converts the image to sRGB color space, which is recommended for best results.

Apply auto-correction with Universal AI Model

Create an engine and call PFC_AutoCorrect().

PFCENGINE* pEngine = PFC_CreateEngine();
// Load AI engine (assuming all dlls and models are in exe folder):
std::string exePath = std::string(argv[0]);
std::string binPath = exePath.substr(0, exePath.find_last_of('/')).c_str();
int aistatus  = PFC_LoadAIEngine(pAiEngine, AI_SCENE_DETECTION | AI_CORRECTIONS | AI_COLOR | AI_FACEMESH, binPath.c_str());

// We set params within AutoCorrect(), so this can start out empty.
PFCPARAM ignoredParam;

int result = PFC_AutoCorrect(&image, NULL, ignoredParam, -1, NULL, false, NULL, pEngine, false);
if (result < APPLY_SUCCESS) {
    printf("Correction failed: %d\n", result);
    PFC_DestroyEngine(pEngine);
    PFC_ReleaseProtectionPath();
    return 1;
}

PFC_DestroyEngine(pEngine);

Save output

Save the corrected image using SaveImageFile().

// return image to original orientation before saving
PFC_TransposeExif(&image, -imageFile.exifOrientation());
if (imageFile.SaveImageFile("output.jpg", 90, true, true)) {
    printf("Saved to: output.jpg\n");
} else {
    printf("Failed to save output\n");
    return 1;
}

Parameters are: output path, JPEG quality (0-100), convert back to original color space, and preserve metadata.

Complete example

Create main.cpp:

#include <stdio.h>

extern "C" {
#include "PerfectlyClearPro.h"
}
#include "PFCImageFile.h"

int main(int argc, char* argv[]) {
    if (argc < 3) {
        printf("Usage: %s <input.jpg> <output.jpg>\n", argv[0]);
        return 1;
    }

    const char* inputFile = argv[1];
    const char* outputFile = argv[2];

    // Activate SDK license
    int licenseStatus = PFC_SetProtectionPath("./perfectly-clear-sdk/sdk_license");
    if (licenseStatus == 0 || licenseStatus == 102 || licenseStatus == 103) {
        printf("SDK license activated successfully\n");
    } else {
        printf("License activation failed (status: %d)\n", licenseStatus);
        return 1;
    }

    // Load image
    PFCImageFile imageFile;
    if (imageFile.LoadImageFile(inputFile, true, NULL) != LOAD_OK) {
        printf("Failed to load image: %s\n", inputFile);
        return 1;
    }

    // Set up image structure
    PFCIMAGE image = {
        imageFile.width,
        imageFile.height,
        imageFile.stride,
        (PFCPIXELFORMAT)imageFile.pfcImageFormat(),
        imageFile.raw_image
    };

    // rotate image according to EXIF orientation tag
    PFC_TransposeExif(&image, imageFile.exifOrientation()); 

    PFCENGINE* pEngine = PFC_CreateEngine();
    // Load AI engine (assuming all dlls and models are in exe folder):
    std::string exePath = std::string(argv[0]);
    std::string binPath = exePath.substr(0, exePath.find_last_of('/')).c_str();
    int aistatus  = PFC_LoadAIEngine(pAiEngine, AI_SCENE_DETECTION | AI_CORRECTIONS | AI_COLOR | AI_FACEMESH, binPath.c_str());

    // We set params within AutoCorrect(), so this can start out empty.
    PFCPARAM ignoredParam;

    int result = PFC_AutoCorrect(&image, NULL, ignoredParam, -1, NULL, false, NULL, pEngine, false);
    if (result < APPLY_SUCCESS) {
        printf("Correction failed: %d\n", result);
        PFC_DestroyEngine(pEngine);
        PFC_ReleaseProtectionPath();
        return 1;
    }

    PFC_DestroyEngine(pEngine);
    printf("Image corrected successfully\n");

    // Cleanup
    PFC_DestroyEngine(pEngine);
    PFC_ReleaseProtectionPath();

    // Save output
    // return image to original orientation before saving
	PFC_TransposeExif(&image, -imageFile.exifOrientation());
    if (imageFile.SaveImageFile(outputFile, 90, true, true)) {
        printf("Saved to: %s\n", outputFile);
    } else {
        printf("Failed to save: %s\n", outputFile);
        return 1;
    }

    return 0;
}

Create Makefile:

CC = g++
SDK_ROOT = ./perfectly-clear-sdk
SDK_BIN = $(SDK_ROOT)/Linux/bin
SDK_HEADER = $(SDK_ROOT)/Linux/header

CFLAGS = -O2 -pthread -D_LINUX -I$(SDK_HEADER) -Wl,-R'$$ORIGIN/perfectly-clear-sdk/Linux/bin'

all: quick-cpp copy-deps

quick-cpp: main.cpp
	$(CC) $(CFLAGS) -m64 -o $@ main.cpp \
		$(SDK_BIN)/libPerfectlyClearPro.a \
		$(SDK_BIN)/libPFCImageFile.a \
		$(SDK_BIN)/libwebp.a \
		$(SDK_BIN)/liblcms2.so.2 \
		$(SDK_BIN)/libexiv2.so.26 \
		-ldl

copy-deps:
	@echo "Copying dependencies..."
	cp $(SDK_BIN)/liblcms2.so.2 .
	cp $(SDK_BIN)/libexiv2.so.26 .
    cp $(SDK_BIN)/*pnn .
	@echo "Build complete!"

clean:
	rm -f quick-cpp *.so*

.PHONY: all copy-deps clean

Build and run:

make
./quick-cpp sample.jpg corrected.jpg

Expected output:

Copying dependencies...
Build complete!
SDK license activated successfully
Image corrected successfully
Saved to: corrected.jpg

PFC-SDK Version 10.7.3.1317 built from df9dbc8727c92fb9b1d2c68b21e60ea9c7229e1a on 03-23-2026.

Copyright © 2026 EyeQ Imaging Inc. All rights reserved.

On this page