import Globals from "./Globals";
import { SKINTONE_TARGET_QUALITY } from "./MakeupSDK";
import { MountFileWithProgress } from "./Utility/MountFileWithProgress";
let resource_loading = null;
/**
* Provides skin tone estimation functionality over multiple frames.
* To enable this module pass the appropriate license to the SDKProvider.
*/
export class SkinToneAnalyzer {
static async Create(modulesManager, skinToneTargetQuality, onProgress) {
const bsc = modulesManager.modules.visage.module;
const quality =
skinToneTargetQuality == SKINTONE_TARGET_QUALITY.MORE_ACCURACY
? bsc.SkinToneTargetQuality.MORE_ACCURACY
: bsc.SkinToneTargetQuality.MORE_SPEED;
const bscAnalyzer = bsc.GetSkinToneAnalysis(quality);
// file loads wrapped so they load lazily, but once-only
if (!resource_loading) {
// NOTE(Straza) byteLen added because if the server uses compression (gzip),
// content-length will not be correct
// there is no standard header for uncompressed size
let resources = [];
if (
quality?.value == bsc.SkinToneTargetQuality.MORE_ACCURACY?.value
) {
resources = [
{
name: "d_st_c.bin",
path:
Globals.pathToAssets + "Assets/Plugins/d_st_c.bin",
byteLen: 1236,
},
{
name: "d_st_m.js.json",
path:
Globals.pathToAssets +
"Assets/Plugins/d_st_m.js.json",
byteLen: 320112,
},
{
name: "d_st_m.js.bin",
path:
Globals.pathToAssets +
"Assets/Plugins/d_st_m.js.bin",
byteLen: 9867712,
},
];
} else {
resources = [
{
name: "st_c.bin",
path: Globals.pathToAssets + "Assets/Plugins/st_c.bin",
byteLen: 1236,
},
{
name: "st_m.js.json",
path:
Globals.pathToAssets +
"Assets/Plugins/st_m.js.json",
byteLen: 39024,
},
{
name: "st_m.js.bin",
path:
Globals.pathToAssets + "Assets/Plugins/st_m.js.bin",
byteLen: 22344672,
},
];
}
let curentprogresses = resources.reduce((acc, x) => {
acc[x.name] = {
contentLength: x.byteLen,
receivedLength: 0,
progress: 0,
done: false,
};
return acc;
}, {});
let total = resources
.map((k) => k.byteLen)
.reduce((partialSum, a) => partialSum + a, 0);
const start = (x) => {};
const progress = (name) => (x) => {
if (!onProgress) return;
curentprogresses[name] = x;
const keys = Object.keys(curentprogresses);
const done = keys.every((k) => curentprogresses[k].done);
let received = 0;
if (done) {
received = total;
} else {
received = keys
.map((k) => curentprogresses[k].receivedLength)
.reduce((partialSum, a) => partialSum + a, 0);
}
onProgress({
contentLength: total,
receivedLength: received,
progress: received / total,
done,
});
};
const resource_promises = resources.map((r) =>
MountFileWithProgress(
bsc.FS,
r.name,
r.path,
progress(r.name),
start,
r.byteLen,
),
);
resource_loading = Promise.all(resource_promises).catch(
() => (resource_loading = null),
);
}
await resource_loading;
return new SkinToneAnalyzer(bscAnalyzer, bsc);
}
constructor(bscAnalyzer, bsc) {
this.bscAnalyzer = bscAnalyzer;
this.bsc = bsc;
}
/**
*@callback SkinToneCallback
*@param {Object} SkinToneAnalyzerResult - Analyzed skin tone
*@param {"MONK_1"|"MONK_2"|"MONK_3"|"MONK_4"|"MONK_5"|"MONK_6"|"MONK_7"|"MONK_8"|"MONK_9"|"MONK_10"|"UNDEFINED"} SkinToneAnalyzerResult.monk - Estimated Monk skin tone
* The monk skin tone scale is a scale describing human skin color.
* The scale consist of 10 categories, where a lower number maps to lighter while a higher to darker skin tones.
*@param {number} SkinToneAnalyzerResult.decimalMonk - Numerical value of predicted monk skin tone.
*@param {Array<number>} SkinToneAnalyzerResult.RGB - This property now represents apparent RGB instead of the original Google RGB representation, of various monk skin tones.
*@param {Array<number>} SkinToneAnalyzerResult.renderRGB - Deprecated: RGB of various monk skin tones that will give more realistic rendering results
*@deprecated SkinToneAnalyzerResult.renderRGB - will be removed in future versions, SkinToneAnalyzerResult.RGB - It will be used as apparent RGB representation of various monk skin tones
*@param {Array<number>} SkinToneAnalyzerResult.HSL - Apparent hue, saturation and light of skin
*@param {number} SkinToneAnalyzerResult.frameIndex - Index of the frame that has been analyzed.
*@param { "OFF"|"OK"|"RECOVERING"|"INIT" } SkinToneAnalyzerResult.status - Indicates tracking status.
*/
/**
* Initiates the skin tone analysis. When a frame has been analyzed a call to the {@link SkinToneCallback}
* function is issued, with the analysis results passed as the argument.
* <br/>
* @param {SkinToneCallback} SkinToneCallback - callback which will be called every time a new frame is analyzed
*/
StartAnalysis(SkinToneCallback) {
const convert = (result) => {
const monk_mapping = new Map([
[this.bsc.MonkScale.Monk_1, "MONK_1"],
[this.bsc.MonkScale.Monk_2, "MONK_2"],
[this.bsc.MonkScale.Monk_3, "MONK_3"],
[this.bsc.MonkScale.Monk_4, "MONK_4"],
[this.bsc.MonkScale.Monk_5, "MONK_5"],
[this.bsc.MonkScale.Monk_6, "MONK_6"],
[this.bsc.MonkScale.Monk_7, "MONK_7"],
[this.bsc.MonkScale.Monk_8, "MONK_8"],
[this.bsc.MonkScale.Monk_9, "MONK_9"],
[this.bsc.MonkScale.Monk_10, "MONK_10"],
[this.bsc.MonkScale.UNDEFINED, "UNDEFINED"],
]);
const status_mapping = new Map([
[this.bsc.TrackStatus.off, "OFF"],
[this.bsc.TrackStatus.init, "INIT"],
[this.bsc.TrackStatus.recovering, "RECOVERING"],
[this.bsc.TrackStatus.ok, "OK"],
]);
let res = {
frameIndex: result.frameIndex,
monk: monk_mapping.get(result.monk),
decimalMonk: result.decimalMonk,
RGB: result.RGB,
renderRGB: result.renderRGB,
HSL: result.HSL,
status: status_mapping.get(result.status),
};
result.delete();
return res;
};
const convertThenCall = (result) => {
SkinToneCallback(convert(result));
};
this.bscAnalyzer.SetCallback(convertThenCall);
this.bscAnalyzer.StartAnalysis();
}
/**
* Stops the skin tone analysis and stops issuing calls to the callback given as a argument to the
* {@link StartAnalysis} function.
* <br/>
*/
StopAnalysis() {
this.bscAnalyzer.StopAnalysis();
}
/**
* Resets the internal state of the skin tone analyzer. This is useful when switching between frames of different people.
*/
Reset() {
this.bscAnalyzer.Reset();
}
}