Perfectly Clear SDK Documentation  9.0.1.315
Examples (Windows)

Sample code for both C and C# projects are provided along with the SDK. The examples below demonstrate the general usage of the Perfectly Clear SDK.

Scenario #1 PCF_AutoCorrect

This is the simplest way to use Perfectly Clear library suite. This protocol is more suitable for developing a server type software project or in scenarios where every image will be corrected with one set of parameters (no user-facing option to adjust correction parameters).

  1. Setup the license protection:
    // if you have a license key, then call PFC_SetProtectionPath with the path
    // to your license files
    int code = PFC_SetProtectionPath("C:\\Protection\\sdk");
    if (bVerbose)
    printf("License Code Status is %d\n", code);
  2. Initialize parameters:
    PFCPARAM param;
    // Initialize PFCPARAM structure with user select preset.
    PFC_SetParam(param, presetID);
  3. Populate image data into PFCIMAGE structure:
    // Populate image information into PFCIMAGE structure.
    im.width = width; // Width of image.
    im.height = height; // Height of image.
    im.stride = pbd->Stride; // Stride of image.
    im.format = ((pbd->PixelFormat == PixelFormat24bppRGB) ? PFC_PixelFormat24bppRGB : PFC_PixelFormat32bppARGB);
    im.data = (unsigned char*)pbd->Scan0; // Location of image data.
  4. Perform full correction using the auto function:
    // Process image with Auto Correct function.
    int status = PFC_AutoCorrectPreset(&im, NULL, presetID);
    // Optionally you can check return status of the correction.
    if (bVerbose)
    {
    if (status == 0)
    {
    printf("Image processed successfully\n");
    }
    else if (status > 0)
    {
    printf(" Pre-calculation on noise returns %d\n", PFCNR_STATUS(status));
    printf(" Core pre-calculation returns %d\n", PFCCORE_STATUS(status));
    printf(" Pre-calculation on face details returns %d\n",PFCFB_STATUS(status));
    printf(" Pre-calculation on red eye returns %d\n", PFCRE_STATUS(status));
    }
    else if (status < 0)
    {
    printf("Image processed failed with return code: %d\n", status);
    }
    }

Scenario #2 Separate Calc and Apply

More advanced way to use Perfectly Clear library suite. This protocol is more suitable for developing a desktop photo editing type software where user parameters will vary under the same session of an image.

  1. Setup the license protection:
    // if you have a license key, then call PFC_SetProtectionPath with the path
    // to your license files
    int code = PFC_SetProtectionPath("C:\\Protection\\sdk");
    if (bVerbose)
    printf("License Code Status is %d\n", code);
  2. Create process engine:
    // Create PFCENGINE instance for use in this session.
    if (bVerbose)
    {
    // Optionally, check status of the created engine instance.
    if (pEngine->status == ENGINESTATUS_OK)
    {
    printf("Engine created successfully.\n");
    }
    else
    {
    {
    printf("Face Beautification library not available.");
    }
    }
    }
  3. Initialize the parameter structure (using default parameter values): The parameters can be imported from a preset file.
    // Optionally, the process parameters can be imported from a .preset file.
    // (The .preset file can be exported from Athentech's desktop applications. e.g. Workbench.)
    std::string profilenameUtf8 = utf8FromW(std::wstring(profilename));
    int ret = PFC_ReadPresets(param, (char*)profilenameUtf8.c_str());
    if (bVerbose)
    printf("PFC_ReadPresets returns %d\n", ret);
    Alternatively the parameters can be initialized using PFC_SetParam() function.
    // Use default parameters.
    PFC_SetParam(param);
    Example:
    // Set process parameters.
    PFCPARAM param;
    if (profilename != NULL)
    {
    // Optionally, the process parameters can be imported from a .preset file.
    // (The .preset file can be exported from Athentech's desktop applications. e.g. Workbench.)
    std::string profilenameUtf8 = utf8FromW(std::wstring(profilename));
    int ret = PFC_ReadPresets(param, (char*)profilenameUtf8.c_str());
    if (bVerbose)
    printf("PFC_ReadPresets returns %d\n", ret);
    if (ret != 0)
    {
    if (ret == -2) // preset file not found
    {
    // Use default parameters if preset file not found.
    PFC_SetParam(param);
    printf("Unable to open preset file %s. Using default settings.\n", profilename);
    }
    else if (ret == -8)
    {
    printf("Invalid preset file.\n");
    exit(0);
    }
    else
    {
    printf("Unable to read preset file.\n");
    exit(0);
    }
    }
    }
    else
    {
    // Use default parameters.
    PFC_SetParam(param);
    }
  4. Populate image data into PFCIMAGE structure:
    // Populate image information into PFCIMAGE structure.
    im.width = width; // Width of image.
    im.height = height; // Height of image.
    im.stride = pbd->Stride; // Stride of image.
    im.format = ((pbd->PixelFormat == PixelFormat24bppRGB) ? PFC_PixelFormat24bppRGB : PFC_PixelFormat32bppARGB);
    im.data = (unsigned char*)pbd->Scan0; // Location of image data.
  5. Perform pre-calculation of image specific profile:
    // Analyze image and obtain image specific profile.
    PFCPROFILE pProfile = PFC_Calc(&im, NULL, pEngine, CALC_ALL, -1, NULL, NULL, bVerbose ? myStatus : NULL);
    // Optinally, check precalc status.
    if (bVerbose)
    {
    if (pProfile->Status != 0)
    {
    if (pProfile->Status & CALC_NR)
    {
    printf(" Pre-calculation on noise returns %d\n", pProfile->NR_Status);
    }
    if (pProfile->Status & CALC_CORE)
    {
    printf(" Core pre-calculation returns %d\n", pProfile->CORE_Status);
    }
    if (pProfile->Status & CALC_FB)
    {
    printf(" Pre-calculation on face details returns %d\n", pProfile->FB_Status);
    }
    if (pProfile->Status & CALC_RE)
    {
    printf(" Pre-calculation on red eye returns %d\n", pProfile->RE_Status);
    }
    }
    printf("Calc returns %d\n", pProfile->Status);
    }
    (pEngine is created from step 1.)
  6. Apply the calculated profile and parameters to the image.
    // Process image with parameters.
    PFCAPPLYSTATUS status = PFC_Apply(&im, pEngine, pProfile, param, bVerbose ? myStatus : NULL);
    // Optionally, check process status.
    if (bVerbose)
    {
    if (status == APPLY_SUCCESS)
    {
    printf("Image processed successfully.\n");
    }
    else if (status > APPLY_SUCCESS)
    {
    int code;
    // Check return code for Noise Removal
    code = NRRETCODE(status);
    printf("Noise removal status %d\n", code);
    // Check return code for Perfectly Clear Core correction
    code = CORERETCODE(status);
    printf("Perfectly Clear correction status %d\n", code);
    // Check return code for Face Beautification
    code = FBRETCODE(status);
    printf("Face beautification status %d\n", code);
    // Check return code for Red Eye Removal
    code = RERETCODE(status);
    printf("Red eye removal status %d\n", code);
    }
    else
    {
    printf("Image processing failed with return code: %d\n", status);
    }
    }
  7. Release resources used by PFCPROFILE:
    // Release Profile
    PFC_ReleaseProfile(pProfile);
  8. Release the engine to release resource:
    // Destroy engine. It is important that PFCENGINE is destroyed after
    // all the profiles are released.

Scenario #3 .NET Core Samples

We have included two samples that run in .NET Core v3.1. These are essentially the same applications as Sample 2 shown above. In DotNetCore_Sample2 - we use standard windows bitmaps to load and save the image data, greatly simplified here:

Bitmap bm = new Bitmap(inname);
Pfc.Calc(ref bm, PerfectlyClearAdapter.PFCFEATURE.CALC_ALL, -1, null);
Pfc.Apply(ref bm, 100);
bm.Save(outname);

In DotNetCore_Sample3, we use the PfCImageFile class instead, shown in a simplified form here:

file.LoadImage(inname);
Pfc.Calc(ref file, PerfectlyClearAdapter.PFCFEATURE.CALC_ALL, -1, null);
Pfc.Apply(ref file, 100);
file.SaveImageFile(outname,90, false, false);

If you are incorporating Perfectly Clear into an application that already is using BitMaps, then Sample2 will make it easy to incorporate Perfectly Clear into it.

Sample 3's use of the PfCImageFile class will handle color space conversion and metadata management automatically

Scenario #4 Using MemoryStreams

In SDK v8.5, we have introduced the ability to read and write image data using .NET native MemoryStreams. If the application you are working on already has image file data loaded into MemoryStreams, this method allows you to use those directly, instead of needing to write image files to disk.

// create a PFCImageFile called 'file' we will load into below
// Read input file to a MemoryStream and check if image opened successfully
// Typically, the MemoryStream would already exist in the application you are developing
MemoryStream inMemoryCopy = new MemoryStream();
using (FileStream fs = File.OpenRead(inname))
{
fs.CopyTo(inMemoryCopy);
}
// remember to set the right position to begin reading
inMemoryCopy.Position = 0;
// Call ExpandImageBuffer to read the MemoryStream and load into the 'file' PFCImageFile
if (file.ExpandImageBuffer(inMemoryCopy, PFCImageFile.PFC_FILETYPE.PFC_JPEG, true, "") != PFCImageFile.PFC_FILE_LOAD_STATUS.LOAD_OK)
{
Console.WriteLine("Unable to open image.");
ShowHelp();
return;
}

Now, you can process the PFCImageFile like in the other samples, the most simple way would be:

Pfc.AutoCorrect(ref file, "my_preset.preset");

Now, the 'file' object has been corrected, so to get the corrected image data back into a MemoryStream:

// Read from the corrected 'file' PFCImageFile and write contents to a MemoryStream
// Perform JPEG compression, color management, and embed EXIF metadata from the original file
using (MemoryStream outStream = file.CompressImageBuffer(PFCImageFile.PFC_FILETYPE.PFC_JPEG, 90, false, true))
{
// seek to the beginning, then write to a file, or pass along to further operations
// within your application
outStream.Seek(0, SeekOrigin.Begin);
FileStream outFile = File.Create(outname);
outStream.CopyTo(outFile);
}

.NET Core Version Issues

In the .NET Core examples, there are PreBuildEvent steps to copy the required libraries into the proper build location. For example:

<PropertyGroup>
<PreBuildEvent>
xcopy /C /E /Y /R ..\..\..\..\..\bin\x64\PerfectlyClearPro.dll .\PerfectlyClearPro.dll*
xcopy /C /E /Y /R ..\..\..\..\..\bin\x64\PerfectlyClearAdapter.dll .
xcopy /C /E /Y /R ..\..\..\..\..\bin\x64\PFCImageFile.dll .
xcopy /C /E /Y /R ..\..\..\..\..\bin\x64\exiv2.dll .
</PreBuildEvent>
</PropertyGroup>

Depending on which version of Visual Studio and .NET Core version you are using, these steps might cause errors when you build. This is due to an extra folder level that Visual Studio builds in some cases. The solution is to remove one set of "..\" from these steps, like this:

<PropertyGroup>
<PreBuildEvent>
xcopy /C /E /Y /R ..\..\..\..\bin\x64\PerfectlyClearPro.dll .\PerfectlyClearPro.dll*
xcopy /C /E /Y /R ..\..\..\..\bin\x64\PerfectlyClearAdapter.dll .
xcopy /C /E /Y /R ..\..\..\..\bin\x64\PFCImageFile.dll .
xcopy /C /E /Y /R ..\..\..\..\bin\x64\exiv2.dll .
</PreBuildEvent>
</PropertyGroup>