import { SdkAnalytics } from "./Analytics";
import { FrameProvider } from "./FrameProvider";
import Progress from "./Utility/Progress";
import { MakeupEngine } from "./MakeupEngine";
import { CreateModulesManager } from "./ModulesManager";
import { SkinToneAnalyzer } from "./SkinToneAnalyzer";
import Globals from "./Globals";
import { DefaultFeatureAndFinishService } from "./DefaultFeatureAndFinishService";
import { FaceTracker } from "./FaceTracker";
import { FaceShapeAnalyzer } from "./FaceShapeAnalyzer";
import { EyeToneAnalyzer } from "./EyeToneAnalyzer";
import { EyeShapeAnalyzer } from "./EyeShapeAnalyzer";
import { LightConditionsAnalyzer } from "./LightConditionsAnalyzer";
import { HairToneAnalyzer } from "./HairToneAnalyzer";
import { CmsMakeupEngine } from "./CmsMakeupEngine";
import { LipShapeAnalyzer } from "./LipShapeAnalyzer";
import { GlobalEngine } from "./GlobalEngine";
/**
* @callback onProgressCallback
* @param {number} progress - a number from 0 to 1 indicating the progress level
*/
/**
* Sets onProgressCallback.
* @function SetOnProgressCallback
* @param {onProgressCallback} onProgressCallback - callback which will be called every time loading progresses
*/
export const SetOnProgressCallback = (onProgressCallback) => {
Progress.SetOnProgressCallback(onProgressCallback);
};
/**
* @typedef {number} SkinToneTargetQuality
**/
/**
* @enum {SkinToneTargetQuality}
*/
export const SKINTONE_TARGET_QUALITY = Object.freeze({
MORE_SPEED: 0,
MORE_ACCURACY: 1,
});
/**
* @typedef {number} TrackingQuality
**/
/**
* @enum {TrackingQuality}
*/
export const TRACKING_QUALITY = Object.freeze({
HIGH: 0,
LOW: 1,
});
/** @type {SDKProvider | undefined} */
let defaultProviderSingleton = undefined;
const DEFAULT_CMS_URL = "https://cms.arbelle.ai:3000/api/v2.0";
/**
* Gets or creates a SDKProvider singleton instance.
*
* @function GetProvider
* @param {string} pathToAssets - url of the folder containing SDK Assets folder
* @param {Object.<string, { license: string, isRaw: boolean }>} licenses - Map between String key and String value where
* key is one of SKINTONE or MAKEUP and value is path to appropriate license for type defined by key.
* @param {TrackingQuality} [trackingQuality=TrackingQuality.HIGH] - sets the quality of internal face tracker used by the SDK. Possible values are: HIGH: 0(default) or LOW: 1
* @param {string} [accessToken=""] - token used to provide access to the services.
* @param {string} [cmsURL="https://cms.arbelle.ai:3000/api/v2.0"] - URL on which analytics requests are being sent. If no URL is provided, the default CMS URL will be used.
* @returns {Promise<SDKProvider>} SDKProvider singleton.
*/
export const GetProvider = async (
pathToAssets,
licenses,
trackingQuality = TRACKING_QUALITY.HIGH,
accessToken = "",
cmsURL = DEFAULT_CMS_URL,
) => {
if (defaultProviderSingleton) {
return defaultProviderSingleton;
}
let sdkAnalytics = null
if (accessToken && cmsURL) {
sdkAnalytics = await SdkAnalytics.create(cmsURL, accessToken);
}
Globals.pathToAssets = pathToAssets;
const modulesManager = await CreateModulesManager(licenses);
const highOrLow = trackingQuality == TRACKING_QUALITY.HIGH ? "high" : "low";
const trackingQualityCfgPath = `${Globals.pathToAssets}Assets/Plugins/Facial Features Tracker - ${highOrLow}.cfg`;
const analyticsConfig =
await modulesManager.modules.visage.module.AnalyticsCollectionConfig.Create(
cmsURL,
accessToken,
(await sdkAnalytics)?.sessionUuid ?? "",
1000,
);
await modulesManager.modules.visage.module.CreateBSCProvider(
trackingQualityCfgPath,
analyticsConfig,
);
const frameProvider = new FrameProvider(modulesManager);
defaultProviderSingleton = new SDKProvider(
modulesManager,
frameProvider,
sdkAnalytics
);
return defaultProviderSingleton;
};
/**
* Provides all makeup|SDK modules.
*/
class SDKProvider {
/**
* @param {*} modulesManager
* @param {FrameProvider} frameProvider
* @param {SdkAnalytics | null} sdkAnalytics
*/
constructor(
modulesManager,
frameProvider,
sdkAnalytics,
) {
/** @type {MakeupEngine | undefined} */
this.makeupEngine = undefined;
/** @type {CmsMakeupEngine | undefined} */
this.cmsMakeupEngine = undefined;
this.modulesManager = modulesManager;
this.frameProvider = frameProvider;
this.sdkAnalytics = sdkAnalytics
if (this.sdkAnalytics) {
window.addEventListener("pagehide", () => {
this.sdkAnalytics?.onUnload();
});
}
// misc services
this.featuresAndFinishesService = new DefaultFeatureAndFinishService();
}
/**
* Gets the FrameProvider instance.
* Use this to pass frames to the makeup engine, skin tone analyzer or face tracker.
*
* @return FrameProvider instance.
*/
GetFrameProvider() {
return this.frameProvider;
}
/**
* Gets the session uuid used by the Analytics API.
*
* @return {string} session uuid.
*/
GetSessionUUID() {
return this.sdkAnalytics?.sessionUuid ?? "";
}
/**
* @deprecated Please use CreateMakeupEngine method.
*
* Gets or creates a MakeUpEngine singleton instance.
*
* @function CreateMakeUpEngine
* @param {number} width - integer width of the output frame
* @param {number} height - integer height of the output frame
* @param {boolean} preserveDrawingBuffer - sets the flag on WebGl (default = false)
* @returns {Promise<MakeupEngine>} MakeUpEngine singleton.
*/
async CreateMakeUpEngine(width, height, preserveDrawingBuffer = false) {
return this.CreateMakeupEngine(width, height, preserveDrawingBuffer);
}
/**
* Gets or creates a MakeupEngine singleton instance.
*
* @function CreateMakeupEngine
* @param {number} width - integer width of the output frame
* @param {number} height - integer height of the output frame
* @param {boolean} preserveDrawingBuffer - sets the flag on WebGl (default = false)
* @returns {Promise<MakeupEngine>} MakeupEngine singleton.
*/
async CreateMakeupEngine(width, height, preserveDrawingBuffer = false) {
if (!GlobalEngine.instance) GlobalEngine.Initialize(
{
frameProvider: this.frameProvider,
modulesManager: this.modulesManager
},
{ width, height },
preserveDrawingBuffer
)
if (this.makeupEngine !== undefined) return this.makeupEngine;
const pathToAssets = Globals.pathToAssets;
const [features, finishes] = await Promise.all([
this.featuresAndFinishesService.GetFeatureTemplates(pathToAssets),
this.featuresAndFinishesService.GetFinishTemplates(pathToAssets),
]);
this.makeupEngine = new MakeupEngine(features,finishes,this.sdkAnalytics)
return this.makeupEngine;
}
/**
* Gets or creates a CmsMakeupEngine singleton instance.
*
* @function CreateCmsMakeupEngine
* @param {number} width - integer width of the output frame
* @param {number} height - integer height of the output frame
* @param {boolean} preserveDrawingBuffer - sets the flag on WebGl (default = false)
* @returns {Promise<CmsMakeupEngine>} MakeupEngine singleton.
*/
async CreateCmsMakeupEngine(
width,
height,
preserveDrawingBuffer = false, //TODO(Nino) rename
) {
if (!GlobalEngine.instance) GlobalEngine.Initialize(
{
frameProvider: this.frameProvider,
modulesManager: this.modulesManager
},
{ width, height },
preserveDrawingBuffer
)
if (this.cmsMakeupEngine !== undefined) return this.cmsMakeupEngine;
const pathToAssets = Globals.pathToAssets;
const [features, finishes] = await Promise.all([
this.featuresAndFinishesService.GetFeatureTemplates(pathToAssets),
this.featuresAndFinishesService.GetFinishTemplates(pathToAssets),
]);
this.cmsMakeupEngine = new CmsMakeupEngine(
features, finishes, this.sdkAnalytics
);
return this.cmsMakeupEngine;
}
/**
* Configures skin tone quality. Must be set before SkinToneAnalyzer.
* is created or it will have no effect
*
* MORE_SPEED: 0(default)
*
* MORE_ACCURACY: 1
*
* @param {SkinToneTargetQuality} skinToneTargetQuality
*/
SetSkinToneTargetQuality(skinToneTargetQuality) {
this.skinToneTargetQuality = skinToneTargetQuality;
}
/**
* @callback onSkinToneProgressCallback
* @param {Object} status - information on current progress
* @param {number} status.contentLength - the whole length of the content in bytes
* @param {number} status.receivedLength - the currently received length in bytes
* @param {number} status.progress - a number from 0 to 1 indicating the progress level
* @param {boolean} status.done - true if the creation process is done, false otherwise
*/
/**
* Gets or creates a SkinToneAnalyzer singleton instance.
* <br/>
*
* @function CreateSkinToneAnalyzer
* @param {undefined | onSkinToneProgressCallback} onProgressCallback - callback which will be called every time loading progresses
* @returns {Promise<SkinToneAnalyzer>}
*/
async CreateSkinToneAnalyzer(onProgressCallback = undefined) {
const analyzer = await SkinToneAnalyzer.Create(
this.modulesManager,
this.skinToneTargetQuality ?? SKINTONE_TARGET_QUALITY.MORE_SPEED,
onProgressCallback,
);
return analyzer;
}
/**
* Gets or creates a SkinToneAnalyzer singleton instance.
* <br/>
*
* @function CreateEyeToneAnalyzer
* @param {undefined | onSkinToneProgressCallback} onProgressCallback - callback which will be called every time loading progresses
* @returns {Promise<EyeToneAnalyzer>}
*/
async CreateEyeToneAnalyzer(onProgressCallback = undefined) {
const analyzer = await EyeToneAnalyzer.Create(
this.modulesManager,
onProgressCallback,
);
return analyzer;
}
/**
* Gets or creates a FaceTracker singleton instance.
* @function CreateFaceTracker
* @returns {Promise<FaceTracker>} FaceTracker singleton.
*/
async CreateFaceTracker() {
const analyzer = await FaceTracker.Create(this.modulesManager);
return analyzer;
}
/**
* Starts creating shape analyzer.
* <br/>
* Returns a promise which will be resolved when analyzer has been created.
* @function CreateFaceShapeAnalyzer
* @param {undefined | onSkinToneProgressCallback} onProgressCallback - callback which will be called every time loading progresses
* @returns {Promise<FaceShapeAnalyzer>}
*/
async CreateFaceShapeAnalyzer(onProgressCallback = undefined) {
const analyzer = await FaceShapeAnalyzer.Create(
this.modulesManager,
onProgressCallback,
);
return analyzer;
}
/**
* Starts creating hair tone analyzer.
* <br/>
* Returns a promise which will be resolved when analyzer has been created.
* @function CreateHairToneAnalyzer
* @param {undefined | onSkinToneProgressCallback} onProgressCallback - callback which will be called every time loading progresses
* @returns {Promise<HairToneAnalyzer>}
*/
async CreateHairToneAnalyzer(onProgressCallback = undefined) {
const analyzer = await HairToneAnalyzer.Create(
this.modulesManager,
onProgressCallback,
);
return analyzer;
}
/**
* Configures face uniformity threshold. Must be set before LightConditionsAnalyzer
* is created or it will have no effect.
*
* @param {number} uniformityThreshold
*/
SetLightConditionsUniformityThreshold(uniformityThreshold) {
this.lightConditionsUniformityThreshold = uniformityThreshold;
}
/**
* Configures face uniformity threshold. Must be set before LightConditionsAnalyzer
* is created or it will have no effect.
*
* @param {number} backlightThreshold
*/
SetLightConditionsBacklightThreshold(backlightThreshold) {
this.lightConditionsBacklightThreshold = backlightThreshold;
}
/**
* Starts creating light conditions analyzer.
* <br/>
* Returns a promise which will be resolved when analyzer has been created.
* @function CreateLightConditionsAnalyzer
* @param {undefined | onSkinToneProgressCallback} onProgressCallback - callback which will be called every time loading progresses
* @returns {Promise<LightConditionsAnalyzer>}
*/
async CreateLightConditionsAnalyzer(onProgressCallback = undefined) {
const analyzer = await LightConditionsAnalyzer.Create(
this.modulesManager,
this.lightConditionsUniformityThreshold,
this.lightConditionsBacklightThreshold,
onProgressCallback,
);
return analyzer;
}
/**
* Starts creating lip shape analyzer.
* <br/>
* Returns a promise which will be resolved when analyzer has been created.
* @function CreateLipShapeAnalyzer
* @param {undefined | onSkinToneProgressCallback} onProgressCallback - callback which will be called every time loading progresses
* @returns {Promise<LipShapeAnalyzer>}
*/
async CreateLipShapeAnalyzer(onProgressCallback = undefined) {
const analyzer = await LipShapeAnalyzer.Create(
this.modulesManager,
onProgressCallback,
);
return analyzer;
}
/**
* Starts creating eye shape analyzer.
* <br/>
* Returns a promise which will be resolved when analyzer has been created.
* @function CreateEyeShapeAnalyzer
* @param {undefined | onSkinToneProgressCallback} onProgressCallback - callback which will be called every time loading progresses
* @returns {Promise<EyeShapeAnalyzer>}
*/
async CreateEyeShapeAnalyzer(onProgressCallback = undefined) {
const analyzer = await EyeShapeAnalyzer.Create(
this.modulesManager,
onProgressCallback,
);
return analyzer;
}
}