EyeQ Docs

Using the C API

In-depth guide to using the Perfectly Clear C API

The C API provides direct access to the Perfectly Clear correction engine. It's available on all supported platforms (Linux, macOS, Windows) and allows full contol of the image correction process.

Core objects

The SDK uses four primary structures that you'll work with in every integration: PFCENGINE for the correction engine, PFCIMAGE for image data, PFCPROFILE for analysis results, and PFCPARAM for correction parameters.

The PFCENGINE structure represents a correction engine instance. Create one engine per thread and reuse it across multiple images for best performance.

typedef struct {
    PFCINTERNAL pEngine;      // Internal engine pointer
    PFCENGINESTATUS status;   // Engine initialization status
} PFCENGINE;

Create and destroy engines with:

PFCENGINE *pEngine = PFC_CreateEngine();
// ... use the engine ...
PFC_DestroyEngine(pEngine);

The PFCIMAGE structure describes an image buffer. You must populate this structure with your image data before processing. This is most commonly done using the PFCImageFile class.

typedef struct {
    int width;               // Pixel width
    int height;              // Pixel height
    int stride;              // Bytes per scanline
    PFCPIXELFORMAT format;   // Pixel format (BGR, RGB, etc.)
    unsigned char *data;     // Pointer to image data
} PFCIMAGE;

Supported pixel formats include:

FormatDescription
PFC_PixelFormat24bppRGB24-bit RGB (B, G, R order)
PFC_PixelFormat24bppBGR24-bit BGR (R, G, B order)
PFC_PixelFormat32bppABGR32-bit with alpha (R, G, B, A order)
PFC_PixelFormat48bppRGB48-bit RGB (16-bit per channel)

The PFCPROFILE (typedef for PFCIMAGEPROFILE*) holds analysis results from PFC_Calc. It contains correction parameters specific to the analyzed image.

typedef struct {
    // Internal pointers (do not modify)
    PFCINTERNAL pPfcParam;
    PFCINTERNAL pNoiseParam;
    PFCINTERNAL pRedEyeParam;
    PFCINTERNAL pSFBParam;
    
    // Status codes from analysis
    PFCCORE_STATUS CORE_Status;
    PFCNR_STATUS NR_Status;
    PFCFB_STATUS FB_Status;
    PFCRE_STATUS RE_Status;
    
    int Status;  // Summary status flags
} PFCIMAGEPROFILE;

Always release profiles when done:

PFCPROFILE pProfile = PFC_Calc(&im, NULL, pEngine, CALC_ALL, -1, NULL, NULL, NULL);
// ... use the profile ...
PFC_ReleaseProfile(pProfile);

The PFCPARAM structure contains all correction parameters. It's a composite of several nested parameter structures. PFCPARAM contain specific settings for each correction and tool. These are most easily modified by using Workbench and exporting the preset content.

typedef struct {
    PFCCOREPARAM core;   // Core corrections (exposure, contrast, etc.)
    PFCFBPARAM fb;       // Face beautification
    PFCNRPARAM nr;       // Noise reduction
    PFCREPARAM re;       // Red eye removal
    PFCV3PARAM v3;       // V3 corrections (LUTs, finishing)
    DP2PARAM dp2;        // AI white balance parameters
} PFCPARAM;

Examples

The following examples demonstrate common integration patterns, from simple single-call correction to more advanced AI-powered workflows.

The simplest approach uses PFC_AutoCorrect, which handles engine creation, analysis, and correction in a single call.

#include "PerfectlyClearPro.h"

// Set up license
PFC_SetProtectionPath("/path/to/sdk_license");

// Set up parameters
PFCPARAM param;
PFC_SetParam(param);  // Use defaults

// Populate image struct
PFCIMAGE im;
im.width = imageWidth;
im.height = imageHeight;
im.stride = imageStride;
im.format = PFC_PixelFormat24bppBGR;
im.data = imageData;

// Correct the image
int status = PFC_AutoCorrect(&im, NULL, param, -1, NULL, FALSE, NULL);

// Release license
PFC_ReleaseProtectionPath();

If your application will re-process the same image with many different settings, then it is more efficient to separate PFC_Calc and PFC_Apply calls.

// Create engine (reuse across images)
PFCENGINE *pEngine = PFC_CreateEngine();

// Analyze image
PFCPROFILE pProfile = PFC_Calc(&im, NULL, pEngine, CALC_ALL, -1, NULL, NULL, NULL);

// Check analysis status
if (pProfile->CORE_Status == PFC_CORE_SUCCESS) {
    // Set up parameters
    PFCPARAM param;
    PFC_SetParam(param);
    
    // Modify parameters if needed
    param.core.iStrength = 120;  // Increase exposure correction
    
    // Apply corrections
    PFCAPPLYSTATUS status = PFC_Apply(&im, pEngine, pProfile, param, NULL);
}

// Clean up
PFC_ReleaseProfile(pProfile);
PFC_DestroyEngine(pEngine);

To enable automatic scene detection and optimized corrections, load the AI models.

PFCENGINE *pEngine = PFC_CreateEngine();

// Load AI models
int aiStatus = PFC_LoadAIEngine(pEngine, 
    AI_SCENE_DETECTION | AI_CORRECTIONS | AI_COLOR | AI_FACEMESH,
    "/path/to/bin");

// Check which features loaded
if (aiStatus & AI_SCENE_DETECTION) {
    printf("Scene detection loaded\n");
}

// Use AutoCorrect with AI engine
PFCPARAM param;
int status = PFC_AutoCorrect(&im, NULL, param, -1, NULL, FALSE, NULL, pEngine, TRUE);

// Get detected scene
int sceneLabel, confidence;
PFC_GetLastSceneProperties(pEngine, &sceneLabel, &confidence);

PFC_DestroyEngine(pEngine);

Common patterns

These patterns address frequent integration scenarios and help you write more efficient code.

Create the engine once and reuse it for multiple images to avoid the overhead of repeated initialization.

PFCENGINE *pEngine = PFC_CreateEngine();
PFC_LoadAIEngine(pEngine, AI_SCENE_DETECTION | AI_CORRECTIONS, binPath);

for (int i = 0; i < imageCount; i++) {
    PFCIMAGE im = loadImage(imagePaths[i]);
    
    PFCPARAM param;
    PFC_SetParam(param);
    
    PFC_AutoCorrect(&im, NULL, param, -1, NULL, FALSE, NULL, pEngine, TRUE);
    
    saveImage(imagePaths[i], &im);
}

PFC_DestroyEngine(pEngine);

Load correction settings exported from Perfectly Clear Workbench using PFC_ReadPresets.

PFCPARAM param;
int result = PFC_ReadPresets(param, "path/to/preset.preset");

if (result == 0) {
    // Successfully loaded
    PFC_AutoCorrect(&im, NULL, param, -1, NULL, FALSE, NULL);
} else if (result == -2) {
    // File not found
} else if (result == -9) {
    // Scene Detection preset - use PFC_LoadScenePresets instead
}

The PFC_Apply and PFC_AutoCorrect functions return a composite status code. Use macros to decode individual component statuses.

int status = PFC_AutoCorrect(&im, NULL, param, -1, NULL, FALSE, NULL);

if (status == APPLY_SUCCESS) {
    printf("Success\n");
} else if (status > 0) {
    // Partial success - decode individual statuses
    printf("Noise removal: %d\n", NRRETCODE(status));
    printf("Core correction: %d\n", CORERETCODE(status));
    printf("Face beautification: %d\n", FBRETCODE(status));
    printf("Red eye removal: %d\n", RERETCODE(status));
} else {
    // Error
    printf("Failed with code: %d\n", status);
}

Pitfalls and solutions

These are common mistakes encountered when integrating the SDK, along with their solutions.

Always release profiles before destroying their associated engine.

// Wrong - causes memory issues
PFC_DestroyEngine(pEngine);
PFC_ReleaseProfile(pProfile);  // Engine already gone!

// Correct
PFC_ReleaseProfile(pProfile);
PFC_DestroyEngine(pEngine);

Always call PFC_ReleaseProtectionPath() when done, especially in error paths.

PFC_SetProtectionPath("/path/to/sdk_license");

// Use try-finally pattern in C++ or ensure all paths call release
// ...

PFC_ReleaseProtectionPath();

Ensure all required .pnn files are in the path passed to PFC_LoadAIEngine. Check the return value to verify which features loaded successfully.

int aiStatus = PFC_LoadAIEngine(pEngine, 
    AI_SCENE_DETECTION | AI_CORRECTIONS | AI_COLOR, 
    binPath);

// Verify each requested feature
if ((aiStatus & AI_SCENE_DETECTION) == 0) {
    printf("Warning: Scene detection not loaded\n");
}

Ensure stride accounts for row padding. Many image formats require rows to be aligned to 4-byte boundaries.

// Correct stride calculation for 4-byte alignment
int bytesPerPixel = 3;  // For 24-bit images
int stride = ((width * bytesPerPixel + 3) / 4) * 4;

PFCENGINE instances are not thread-safe. Create one engine per thread for parallel processing.

// Each thread needs its own engine
void processThread(void* data) {
    PFCENGINE *pEngine = PFC_CreateEngine();
    PFC_LoadAIEngine(pEngine, AI_SCENE_DETECTION | AI_CORRECTIONS, binPath);
    
    // Process images on this thread
    
    PFC_DestroyEngine(pEngine);
}

PFC-SDK Version 10.7.2.1269 built from 4fa849d8101945eea725a08dd0dae5101f090fa0 on 11-10-2025.

Copyright © 2026 EyeQ Imaging Inc. All rights reserved.

On this page