@webarkit/jsartoolkit-nft
Version:
Emscripten port of ARToolKit5 to JavaScript. It is a lighter version of Jsartoolkit5 with only NFT markerless support
529 lines (452 loc) • 17.3 kB
text/typescript
/*
* ARToolkitNFT_simd.ts
* JSARToolKitNFT
*
* This file is part of JSARToolKitNFT - WebARKit.
*
* JSARToolKitNFT is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JSARToolKitNFT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JSARToolKitNFT. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent modules, and to
* copy and distribute the resulting executable under terms of your choice,
* provided that you also meet, for each linked independent module, the terms and
* conditions of the license of that module. An independent module is a module
* which is neither derived from nor based on this library. If you modify this
* library, you may extend this exception to your version of the library, but you
* are not obligated to do so. If you do not wish to do so, delete this exception
* statement from your version.
*
* Copyright 2020 WebARKit.
*
* Author(s): Walter Perdan @kalwalt https://github.com/kalwalt
*
*/
import { initARToolkitNFT } from "./factoryFunctions/initARToolkitNFT_simd";
import { IARToolkitNFT } from "./abstractions/IARToolkitNFT";
import { INFTMarkerInfo } from "./abstractions/CommonInterfaces";
import Utils from "./Utils";
import packageJson from "../package.json";
const { version } = packageJson;
const UNKNOWN_MARKER = -1;
const NFT_MARKER = 0;
export class ARToolkitNFT implements IARToolkitNFT {
/**
* static properties
*/
static get UNKNOWN_MARKER() {
return UNKNOWN_MARKER;
}
static get NFT_MARKER() {
return NFT_MARKER;
}
/* errors */
static ERROR_MARKER_INDEX_OUT_OF_BOUNDS: number;
/* arDebug */
static AR_DEBUG_DISABLE: number;
static AR_DEBUG_ENABLE: number;
static AR_DEFAULT_DEBUG_MODE: number;
/* for arlabelingThresh */
static AR_DEFAULT_LABELING_THRESH: number;
/* for arImageProcMode */
static AR_IMAGE_PROC_FRAME_IMAGE: number;
static AR_IMAGE_PROC_FIELD_IMAGE: number;
static AR_DEFAULT_IMAGE_PROC_MODE: number;
/* for arGetTransMat */
static AR_MAX_LOOP_COUNT: number;
static AR_LOOP_BREAK_THRESH: number;
/* Enums */
static AR_LOG_LEVEL_DEBUG: number;
static AR_LOG_LEVEL_INFO: number;
static AR_LOG_LEVEL_WARN: number;
static AR_LOG_LEVEL_ERROR: number;
static AR_LOG_LEVEL_REL_INFO: number;
static AR_LABELING_THRESH_MODE_MANUAL: number;
static AR_LABELING_THRESH_MODE_AUTO_MEDIAN: number;
static AR_LABELING_THRESH_MODE_AUTO_OTSU: number;
static AR_LABELING_THRESH_MODE_AUTO_ADAPTIVE: number;
static AR_MARKER_INFO_CUTOFF_PHASE_NONE: number;
static AR_MARKER_INFO_CUTOFF_PHASE_PATTERN_EXTRACTION: number;
static AR_MARKER_INFO_CUTOFF_PHASE_MATCH_GENERIC: number;
static AR_MARKER_INFO_CUTOFF_PHASE_MATCH_CONTRAST: number;
static AR_MARKER_INFO_CUTOFF_PHASE_MATCH_BARCODE_NOT_FOUND: number;
static AR_MARKER_INFO_CUTOFF_PHASE_MATCH_BARCODE_EDC_FAIL: number;
static AR_MARKER_INFO_CUTOFF_PHASE_MATCH_CONFIDENCE: number;
static AR_MARKER_INFO_CUTOFF_PHASE_POSE_ERROR: number;
static AR_MARKER_INFO_CUTOFF_PHASE_POSE_ERROR_MULTI: number;
static AR_MARKER_INFO_CUTOFF_PHASE_HEURISTIC_TROUBLESOME_MATRIX_CODES: number;
private instance: any;
private markerNFTCount: number;
private cameraCount: number;
private version: string;
public NFTMarkerInfo: {
error: number;
found: number;
id: number;
pose: Float64Array;
};
public FS: any;
public StringList: any;
public nftMarkers: any;
// construction
/**
* The ARToolkitNFT constructor. It has no arguments.
* These properties are initialized:
* - instance
* - markerNFTCount
* - cameraCount
* - version
* A message is displayed in the browser console during the intitialization, for example:
* "ARToolkitNFT 1.5.0"
*/
constructor() {
// reference to WASM module
this.instance;
this.markerNFTCount = 0;
this.cameraCount = 0;
this.version = version;
console.info("ARToolkitNFT ", this.version);
}
// ---------------------------------------------------------------------------
// initialization
/**
* Init the class injecting the Wasm Module, link the instanced methods and
* create a global artoolkitNFT variable.
* @return {object} the this object
*/
public async init() {
const instance = await initARToolkitNFT();
this.instance = new instance.ARToolKitNFT();
this.FS = instance.FS;
this.StringList = instance.StringList;
this.nftMarkers = instance.nftMarkers;
ARToolkitNFT.ERROR_MARKER_INDEX_OUT_OF_BOUNDS =
instance.ERROR_MARKER_INDEX_OUT_OF_BOUNDS;
ARToolkitNFT.AR_DEBUG_DISABLE = instance.AR_DEBUG_DISABLE;
ARToolkitNFT.AR_DEBUG_ENABLE = instance.AR_DEBUG_ENABLE;
ARToolkitNFT.AR_DEFAULT_DEBUG_MODE = instance.AR_DEFAULT_DEBUG_MODE;
/* for arlabelingThresh */
ARToolkitNFT.AR_DEFAULT_LABELING_THRESH =
instance.AR_DEFAULT_LABELING_THRESH;
/* for arImageProcMode */
ARToolkitNFT.AR_IMAGE_PROC_FRAME_IMAGE = instance.AR_IMAGE_PROC_FRAME_IMAGE;
ARToolkitNFT.AR_IMAGE_PROC_FIELD_IMAGE = instance.AR_IMAGE_PROC_FIELD_IMAGE;
ARToolkitNFT.AR_DEFAULT_IMAGE_PROC_MODE =
instance.AR_DEFAULT_IMAGE_PROC_MODE;
/* for arGetTransMat */
ARToolkitNFT.AR_MAX_LOOP_COUNT = instance.AR_MAX_LOOP_COUNT;
ARToolkitNFT.AR_LOOP_BREAK_THRESH = instance.AR_LOOP_BREAK_THRESH;
/* Enums */
ARToolkitNFT.AR_LOG_LEVEL_DEBUG = instance.AR_LOG_LEVEL_DEBUG;
ARToolkitNFT.AR_LOG_LEVEL_INFO = instance.AR_LOG_LEVEL_INFO;
ARToolkitNFT.AR_LOG_LEVEL_WARN = instance.AR_LOG_LEVEL_WARN;
ARToolkitNFT.AR_LOG_LEVEL_ERROR = instance.AR_LOG_LEVEL_ERROR;
ARToolkitNFT.AR_LOG_LEVEL_REL_INFO = instance.AR_LOG_LEVEL_REL_INFO;
ARToolkitNFT.AR_LABELING_THRESH_MODE_MANUAL =
instance.AR_LABELING_THRESH_MODE_MANUAL;
ARToolkitNFT.AR_LABELING_THRESH_MODE_AUTO_MEDIAN =
instance.AR_LABELING_THRESH_MODE_AUTO_MEDIAN;
ARToolkitNFT.AR_LABELING_THRESH_MODE_AUTO_OTSU =
instance.AR_LABELING_THRESH_MODE_AUTO_OTSU;
ARToolkitNFT.AR_LABELING_THRESH_MODE_AUTO_ADAPTIVE =
instance.AR_LABELING_THRESH_MODE_AUTO_ADAPTIVE;
ARToolkitNFT.AR_MARKER_INFO_CUTOFF_PHASE_NONE =
instance.AR_MARKER_INFO_CUTOFF_PHASE_NONE;
ARToolkitNFT.AR_MARKER_INFO_CUTOFF_PHASE_PATTERN_EXTRACTION =
instance.AR_MARKER_INFO_CUTOFF_PHASE_PATTERN_EXTRACTION;
ARToolkitNFT.AR_MARKER_INFO_CUTOFF_PHASE_MATCH_GENERIC =
instance.AR_MARKER_INFO_CUTOFF_PHASE_MATCH_GENERIC;
ARToolkitNFT.AR_MARKER_INFO_CUTOFF_PHASE_MATCH_CONTRAST =
instance.AR_MARKER_INFO_CUTOFF_PHASE_MATCH_CONTRAST;
ARToolkitNFT.AR_MARKER_INFO_CUTOFF_PHASE_MATCH_BARCODE_NOT_FOUND =
instance.AR_MARKER_INFO_CUTOFF_PHASE_MATCH_BARCODE_NOT_FOUND;
ARToolkitNFT.AR_MARKER_INFO_CUTOFF_PHASE_MATCH_BARCODE_EDC_FAIL =
instance.AR_MARKER_INFO_CUTOFF_PHASE_MATCH_BARCODE_EDC_FAIL;
ARToolkitNFT.AR_MARKER_INFO_CUTOFF_PHASE_MATCH_CONFIDENCE =
instance.AR_MARKER_INFO_CUTOFF_PHASE_MATCH_CONFIDENCE;
ARToolkitNFT.AR_MARKER_INFO_CUTOFF_PHASE_POSE_ERROR =
instance.AR_MARKER_INFO_CUTOFF_PHASE_POSE_ERROR;
ARToolkitNFT.AR_MARKER_INFO_CUTOFF_PHASE_POSE_ERROR_MULTI =
instance.AR_MARKER_INFO_CUTOFF_PHASE_POSE_ERROR_MULTI;
ARToolkitNFT.AR_MARKER_INFO_CUTOFF_PHASE_HEURISTIC_TROUBLESOME_MATRIX_CODES =
instance.AR_MARKER_INFO_CUTOFF_PHASE_HEURISTIC_TROUBLESOME_MATRIX_CODES;
return this;
}
public setup(width: number, height: number, cameraId: number): number {
return this.instance.setup(width, height, cameraId);
}
public teardown(): void {
this.instance.teardown();
}
public setupAR2(): void {
this.instance.setupAR2();
}
public setDebugMode(mode: boolean): number {
return this.instance.setDebugMode(mode);
}
public getDebugMode(): boolean {
return this.instance.getDebugMode();
}
public getProcessingImage(): number {
return this.instance.getProcessingImage();
}
public detectNFTMarker(): number {
return this.instance.detectNFTMarker();
}
public getNFTMarker(markerIndex: number): INFTMarkerInfo {
return this.instance.getNFTMarker(markerIndex);
}
public getNFTData(index: number): object {
return this.instance.getNFTData(index);
}
public setLogLevel(mode: boolean): number {
return this.instance.setLogLevel(mode);
}
public getLogLevel(): number {
return this.instance.getLogLevel();
}
public setProjectionNearPlane(value: number): void {
this.instance.setProjectionNearPlane(value);
}
public getProjectionNearPlane(): number {
return this.instance.getProjectionNearPlane();
}
public setProjectionFarPlane(value: number): void {
this.instance.setProjectionFarPlane(value);
}
public getProjectionFarPlane(): number {
return this.instance.getProjectionFarPlane();
}
public setThresholdMode(mode: number): number {
return this.instance.setThresholdMode(mode);
}
public getThresholdMode(): number {
return this.instance.getThresholdMode();
}
public setThreshold(threshold: number): number {
return this.instance.setThreshold(threshold);
}
public getThreshold(): number {
return this.instance.getThreshold();
}
public setImageProcMode(mode: number): number {
return this.instance.setImageProcMode(mode);
}
public getImageProcMode(): number {
return this.instance.getImageProcMode();
}
public getCameraLens(): any {
return this.instance.getCameraLens();
}
public passVideoData(
videoFrame: Uint8ClampedArray,
videoLuma: Uint8Array,
lumaInternal: boolean,
): void {
this.instance.passVideoData(videoFrame, videoLuma, lumaInternal);
}
// ---------------------------------------------------------------------------
// public accessors
//----------------------------------------------------------------------------
/**
* Load the camera, this is an important and required step, Internally fill
* the ARParam struct.
* @param {Uint8Array|string} urlOrData: the camera parameter, usually a path to a .dat file
* @return {Promise<number>} a promise that resolves to a number, the internal id.
*/
public async loadCamera(urlOrData: Uint8Array | string): Promise<number> {
const target = "/camera_param_" + this.cameraCount++;
let data: Uint8Array;
if (urlOrData instanceof Uint8Array) {
// assume preloaded camera params
data = urlOrData;
} else {
// fetch data via HTTP
try {
data = await Utils.fetchRemoteData(urlOrData);
} catch (error) {
throw new Error("Error in loadCamera function: ", error);
}
}
Utils._storeDataFile(data, target, this);
// return the internal marker ID
return this.instance._loadCamera(target);
}
/**
* Load the NFT Markers (.fset, .iset and .fset3) in the code, Must be provided
* the url of the file without the extension. If fails to load it raise an error.
* @param {Array<string>} urls array of urls of the descriptors files without ext
* @param {function} callback the callback to retrieve the ids.
* @param {function} onError2 the error callback.
* @return {Array<number>} an array of ids.
*/
public addNFTMarkers(
urls: Array<string | Array<string>>,
callback: (filename: number[]) => void,
onError2: (errorNumber: number) => void,
): Array<number> {
const prefixes: any = [];
let pending = urls.length * 3;
const onSuccess = (filename: Uint8Array) => {
pending -= 1;
if (pending === 0) {
const vec = new this.StringList();
const markerIds = [];
for (let i = 0; i < prefixes.length; i++) {
vec.push_back(prefixes[i]);
}
const ret = this.instance._addNFTMarkers(vec);
for (let i = 0; i < ret.size(); i++) {
markerIds.push(ret.get(i));
}
console.log("add nft marker ids: ", markerIds);
if (callback) callback(markerIds);
}
};
const onError = (filename: string, errorNumber?: number) => {
console.log("failed to load: ", filename);
onError2(errorNumber);
};
const loadZFT = (prefix: any) => {
const marker_num = prefix.substring(11);
const prefixTemp = "/tempMarkerNFT_" + marker_num;
const response = this.instance._decompressZFT(prefix, prefixTemp);
let contentIsetUint8 = this.FS.readFile(prefixTemp + ".iset");
let contentFsetUint8 = this.FS.readFile(prefixTemp + ".fset");
let contentFset3Uint8 = this.FS.readFile(prefixTemp + ".fset3");
this.FS.unlink(prefixTemp + ".iset");
this.FS.unlink(prefixTemp + ".fset");
this.FS.unlink(prefixTemp + ".fset3");
let hexStrIset = Utils.Uint8ArrayToStr(contentIsetUint8);
let hexStrFset = Utils.Uint8ArrayToStr(contentFsetUint8);
let hexStrFset3 = Utils.Uint8ArrayToStr(contentFset3Uint8);
let contentIset = new Uint8Array(
hexStrIset.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)),
);
let contentFset = new Uint8Array(
hexStrFset.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)),
);
let contentFset3 = new Uint8Array(
hexStrFset3.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)),
);
Utils._storeDataFile(contentFset, prefix + ".fset", this);
Utils._storeDataFile(contentIset, prefix + ".iset", this);
Utils._storeDataFile(contentFset3, prefix + ".fset3", this);
onSuccess(contentFset);
};
const onSuccessZFT = function () {
loadZFT(arguments[1]);
//onSuccess(contentFset);
};
let Ids: Array<number> = [];
urls.forEach((element, index) => {
const prefix = "/markerNFT_" + this.markerNFTCount;
prefixes.push(prefix);
if (Array.isArray(element)) {
element.forEach((url) => {
const filename = prefix + "." + url.split(".").pop();
this.ajax(
url,
filename,
onSuccess.bind(filename),
onError.bind(filename),
prefix,
);
});
this.markerNFTCount += 1;
} else {
const filename1 = prefix + ".fset";
const filename2 = prefix + ".iset";
const filename3 = prefix + ".fset3";
const filename4 = prefix + ".zft";
let type = Utils.checkZFT(element + ".zft");
if (type) {
pending -= 2;
this.ajax(
element + ".zft",
filename4,
onSuccessZFT.bind(filename4),
onError.bind(filename4),
prefix,
);
} else {
this.ajax(
element + ".fset",
filename1,
onSuccess.bind(filename1),
onError.bind(filename1),
prefix,
);
this.ajax(
element + ".iset",
filename2,
onSuccess.bind(filename2),
onError.bind(filename2),
prefix,
);
this.ajax(
element + ".fset3",
filename3,
onSuccess.bind(filename3),
onError.bind(filename3),
prefix,
);
}
this.markerNFTCount += 1;
}
Ids.push(index);
});
return Ids;
}
// ---------------------------------------------------------------------------
// implementation
/**
* Used internally by the addNFTMarkers method
* @param {string} url url of the marker.
* @param {string} target the target of the marker.
* @param {function} callback callback to get the binary data.
* @param {function} errorCallback the error callback.
* @param {string} prefix the prefix for the marker.
*/
private ajax(
url: string,
target: string,
callback: (byteArray: Uint8Array) => void,
errorCallback: (url: string, message: number) => void,
prefix: string,
) {
const oReq = new XMLHttpRequest();
oReq.open("GET", url, true);
oReq.responseType = "arraybuffer"; // blob arraybuffer
const writeByteArrayToFS = (
target: string,
byteArray: Uint8Array,
callback: (byteArray: Uint8Array, prefix: string) => void,
prefix: string,
) => {
Utils._storeDataFile(byteArray, target, this);
callback(byteArray, prefix);
};
oReq.onload = function () {
if (this.status == 200) {
const arrayBuffer = oReq.response;
const byteArray = new Uint8Array(arrayBuffer);
writeByteArrayToFS(target, byteArray, callback, prefix);
} else {
errorCallback(url, this.status);
}
};
oReq.send();
}
}