UNPKG

threear

Version:

A marker based Augmented Reality library for Three.js

1,139 lines (1,044 loc) 38.9 kB
import ARToolKit from "./ARToolKit"; /** * The ARController is the main object for doing AR marker detection with JSARToolKit. * * To use an ARController, you need to tell it the dimensions to use for the AR processing canvas and * pass it an ARCameraParam to define the camera parameters to use when processing images. * The ARCameraParam defines the lens distortion and aspect ratio of the camera used. * See https://www.artoolworks.com/support/library/Calibrating_your_camera for * more information about AR camera parameteters and how to make and use them. * * If you pass an image as the first argument, the ARController uses that as the image to process, * using the dimensions of the image as AR processing canvas width and height. If the first argument * to ARController is an image, the second argument is used as the camera param. * * The camera parameters argument can be either an ARCameraParam or an URL to a camera definition file. * If the camera argument is an URL, it is loaded into a new ARCameraParam, and the ARController dispatches * a "load" event and calls the onload method if it is defined. * * @exports ARController * @constructor * @param {number} width The width of the images to process. * @param {number} height The height of the images to process. * @param {ARCameraParam | string} camera The ARCameraParam to use for image processing. * If this is a string, the ARController treats it as an URL and tries to load it as a * ARCameraParam definition file, calling ARController#onload on success. */ export class ARToolKitController { public ctx: CanvasRenderingContext2D | null; public canvas: HTMLCanvasElement; public videoWidth: any; public videoHeight: any; public orientation: string; private framepointer: any; private id: number; private listeners: any; private image: any; private patternMarkers: any; private barcodeMarkers: any; private transformMat: any; private defaultMarkerWidth: number; private cameraParam: any; private markerTransformMat: any; private _bwpointer: any; private framesize: any; private dataHeap: any; private cameraMat: any; private onload: any; private contextError = "Canvas 2D Context was not available"; constructor(width: any, height: any, camera: any) { let w = width; let h = height; this.orientation = "landscape"; this.listeners = {}; // TODO: What is going on will all these types? if (typeof width !== "number") { const image = width; camera = height; w = image.videoWidth || image.width; h = image.videoHeight || image.height; this.image = image; } this.defaultMarkerWidth = 1; this.patternMarkers = {}; this.barcodeMarkers = {}; this.transformMat = new Float32Array(16); this.canvas = document.createElement("canvas"); this.canvas.width = w; this.canvas.height = h; this.ctx = this.canvas.getContext("2d"); if (this.ctx === null) { throw Error("Could not get 2D Context for canvas element"); } this.videoWidth = w; this.videoHeight = h; this.id = -1; // TODO: Quick solution to keep TSC happy this.cameraParam = camera; this._initialize(); } /** * Destroys the ARController instance and frees all associated resources. * After calling dispose, the ARController can't be used any longer. Make a new one if you need one. * Calling this avoids leaking Emscripten memory, which may be important if you"re * using multiple ARControllers. */ public dispose() { ARToolKit.teardown(this.id); for (const key in this) { if (key) { delete this[key]; } } } /** * Detects markers in the given image. The process method dispatches marker detection events during its run. * * The marker detection process proceeds by first dispatching a markerNum event that tells you how many * markers were found in the image. Next, a getMarker event is dispatched for each found marker square. * finally, getMultiMarker is dispatched for every found multimarker * followed by getMultiMarkerSub events * dispatched for each of the markers in the multimarker. * arController.addEventListener("markerNum", function(ev) { * console.log("Detected " + ev.data + " markers.") * }); * arController.addEventListener("getMarker", function(ev) { * console.log("Detected marker with ids:", ev.data.marker.id, ev.data.marker.idPatt, ev.data.marker.idMatrix); * console.log("Marker data", ev.data.marker); * console.log("Marker transform matrix:", [].join.call(ev.data.matrix, ", ")); * }); * arController.addEventListener("getMultiMarker", function(ev) { * console.log("Detected multimarker with id:", ev.data.multiMarkerId); * }); * arController.addEventListener("getMultiMarkerSub", function(ev) { * console.log("Submarker for " + ev.data.multiMarkerId, ev.data.markerIndex, ev.data.marker); * }); * arController.process(image); * If no image is given, defaults to this.image. * If the debugSetup has been called, draws debug markers on the debug canvas. * @param {HTMLImageElement|HTMLVideoElement} [image] The image to process [optional]. */ public process(image: HTMLImageElement | HTMLVideoElement) { this.detectMarker(image); const markerNum = this.getMarkerNum(); for (const k in this.patternMarkers) { if (k !== undefined) { const o = this.patternMarkers[k]; o.inPrevious = o.inCurrent; o.inCurrent = false; } } for (const k in this.barcodeMarkers) { if (k !== undefined) { const o = this.barcodeMarkers[k]; o.inPrevious = o.inCurrent; o.inCurrent = false; } } let visible; let multiEachMarkerInfo; for (let i = 0; i < markerNum; i++) { const markerInfo = this.getMarker(i); let markerType = ARToolKit.UNKNOWN_MARKER; visible = this.trackPatternMarkerId(-1); if ( markerInfo.idPatt > -1 && (markerInfo.id === markerInfo.idPatt || markerInfo.idMatrix === -1) ) { visible = this.trackPatternMarkerId(markerInfo.idPatt); markerType = ARToolKit.PATTERN_MARKER; if (markerInfo.dir !== markerInfo.dirPatt) { this.setMarkerInfoDir(i, markerInfo.dirPatt); } } else if (markerInfo.idMatrix > -1) { visible = this.trackBarcodeMarkerId(markerInfo.idMatrix); markerType = ARToolKit.BARCODE_MARKER; if (markerInfo.dir !== markerInfo.dirMatrix) { this.setMarkerInfoDir(i, markerInfo.dirMatrix); } } if (markerType !== ARToolKit.UNKNOWN_MARKER && visible.inPrevious) { this.getTransMatSquareCont( i, visible.markerWidth, visible.matrix, visible.matrix ); } else { this.getTransMatSquare(i, visible.markerWidth, visible.matrix); } visible.inCurrent = true; this.transMatToGLMat(visible.matrix, this.transformMat); this.dispatchEvent({ name: "getMarker", target: this, data: { index: i, type: markerType, marker: markerInfo, matrix: this.transformMat } }); } const multiMarkerCount = this.getMultiMarkerCount(); for (let i = 0; i < multiMarkerCount; i++) { const subMarkerCount = this.getMultiMarkerPatternCount(i); visible = false; ARToolKit.getTransMatMultiSquareRobust(this.id, i); this.transMatToGLMat(this.markerTransformMat, this.transformMat); for (let j = 0; j < subMarkerCount; j++) { multiEachMarkerInfo = this.getMultiEachMarker(i, j); if (multiEachMarkerInfo.visible >= 0) { visible = true; this.dispatchEvent({ name: "getMultiMarker", target: this, data: { multiMarkerId: i, matrix: this.transformMat } }); break; } } if (visible) { for (let j = 0; j < subMarkerCount; j++) { multiEachMarkerInfo = this.getMultiEachMarker(i, j); this.transMatToGLMat(this.markerTransformMat, this.transformMat); this.dispatchEvent({ name: "getMultiMarkerSub", target: this, data: { multiMarkerId: i, markerIndex: j, marker: multiEachMarkerInfo, matrix: this.transformMat } }); } } } if (this._bwpointer) { this.debugDraw(); } } /** * Adds the given pattern marker ID to the index of tracked IDs. * Sets the markerWidth for the pattern marker to markerWidth. * Used by process() to implement continuous tracking, * keeping track of the marker's transformation matrix * and customizable marker widths. * @param {number} id ID of the pattern marker to track. * @param {number} [markerWidth] The width of the marker to track. * @return {Object} The marker tracking object. */ public trackPatternMarkerId(id: number, markerWidth?: number) { let obj = this.patternMarkers[id]; if (!obj) { this.patternMarkers[id] = obj = { inPrevious: false, inCurrent: false, matrix: new Float32Array(12), markerWidth: markerWidth || this.defaultMarkerWidth }; } if (markerWidth) { obj.markerWidth = markerWidth; } return obj; } /** * Adds the given barcode marker ID to the index of tracked IDs. * Sets the markerWidth for the pattern marker to markerWidth. * Used by process() to implement continuous tracking, * keeping track of the marker"s transformation matrix * and customizable marker widths. * @param {number} id ID of the barcode marker to track. * @param {number} [markerWidth] The width of the marker to track. * @return {Object} The marker tracking object. */ public trackBarcodeMarkerId(id: number, markerWidth?: number) { let obj = this.barcodeMarkers[id]; if (!obj) { this.barcodeMarkers[id] = obj = { inPrevious: false, inCurrent: false, matrix: new Float32Array(12), markerWidth: markerWidth || this.defaultMarkerWidth }; } if (markerWidth) { obj.markerWidth = markerWidth; } return obj; } /** * Returns the number of multimarkers registered on this ARController. * @return {number} Number of multimarkers registered. */ public getMultiMarkerCount() { return ARToolKit.getMultiMarkerCount(this.id); } /** * Returns the number of markers in the multimarker registered for the given multiMarkerId. * @param {number} multiMarkerId The id number of the multimarker to access. Given by loadMultiMarker. * @return {number} Number of markers in the multimarker. Negative value indicates failure to find the multimarker. */ public getMultiMarkerPatternCount(multiMarkerId: number) { return ARToolKit.getMultiMarkerNum(this.id, multiMarkerId); } /** * Add an event listener on this ARController for the named event, calling the callback function * whenever that event is dispatched. * * Possible events are: * - getMarker - dispatched whenever process() finds a square marker * - getMultiMarker - dispatched whenever process() finds a visible registered multimarker * - getMultiMarkerSub - dispatched by process() for each marker in a visible multimarker * - load - dispatched when the ARController is ready to use (useful if passing in a camera URL in the constructor) * @param {string} name Name of the event to listen to. * @param {function} callback Callback function to call when an event with the given name is dispatched. */ public addEventListener(name: string, callback: (event: any) => any) { if (!this.listeners[name]) { this.listeners[name] = []; } this.listeners[name].push(callback); } /** * Remove an event listener from the named event. * @param {string} name Name of the event to stop listening to. * @param {function} callback Callback function to remove from the listeners of the named event. */ public removeEventListener(name: string, callback: () => any) { if (this.listeners[name]) { const index = this.listeners[name].indexOf(callback); if (index > -1) { this.listeners[name].splice(index, 1); } } } /** * Dispatches the given event to all registered listeners on event.name. * @param {Object} event Event to dispatch. */ public dispatchEvent(event: any) { const listeners = this.listeners[event.name]; if (listeners) { for (let i = 0; i < listeners.length; i++) { listeners[i].call(this, event); } } } /** * Sets up a debug canvas for the AR detection. Draws a red marker on top of each detected square in the image. * The debug canvas is added to document.body. */ public debugSetup() { document.body.appendChild(this.canvas); this.setDebugMode(1); this._bwpointer = this.getProcessingImage(); } /** * Loads a pattern marker from the given URL and calls the onSuccess callback with the UID of the marker. * arController.loadMarker(markerURL, onSuccess, onError); * @param {string} markerURL - The URL of the marker pattern file to load. * @param {function} onSuccess - The success callback. Called with the id of the loaded marker on a successful load. * @param {function} onError - The error callback. Called with the encountered error if the load fails. */ public loadMarker( markerURL: string, onSuccess: (id: number) => any, onError: (err: any) => any ) { return ARToolKit.addMarker(this.id, markerURL, onSuccess, onError); } /** * Loads a multimarker from the given URL and calls the onSuccess callback with the UID of the marker. * arController.loadMultiMarker(markerURL, onSuccess, onError); * @param {string} markerURL - The URL of the multimarker pattern file to load. * @param {function} onSuccess - The success callback. Called with the id and the * number of sub-markers of the loaded marker on a successful load. * @param {function} onError - The error callback. Called with the encountered error if the load fails. */ public loadMultiMarker( markerURL: string, onSuccess: () => any, onError: () => any ) { return ARToolKit.addMultiMarker(this.id, markerURL, onSuccess, onError); } /** * Populates the provided float array with the current transformation for the specified marker. After * a call to detectMarker, all marker information will be current. Marker transformations can then be * checked. * @param {number} markerUID The unique identifier (UID) of the marker to query * @param {number} markerWidth The width of the marker * @param {Float64Array} dst The float array to populate with the 3x4 marker transformation matrix * @return {Float64Array} The dst array. */ public getTransMatSquare( markerUID: number, markerWidth: number, dst: Float64Array ) { ARToolKit.getTransMatSquare(this.id, markerUID, markerWidth); dst.set(this.markerTransformMat); return dst; } /** * Populates the provided float array with the current transformation for the specified marker, using * previousMarkerTransform as the previously detected transformation. After * a call to detectMarker, all marker information will be current. Marker transformations can then be * checked. * @param {number} markerUID The unique identifier (UID) of the marker to query * @param {number} markerWidth The width of the marker * @param {Float64Array} previousMarkerTransform The float array to use as the previous * 3x4 marker transformation matrix * @param {Float64Array} dst The float array to populate with the 3x4 marker transformation matrix * @return {Float64Array} The dst array. */ public getTransMatSquareCont( markerUID: number, markerWidth: number, previousMarkerTransform: Float64Array, dst: Float64Array ) { this.markerTransformMat.set(previousMarkerTransform); ARToolKit.getTransMatSquareCont(this.id, markerUID, markerWidth); dst.set(this.markerTransformMat); return dst; } /** * Populates the provided float array with the current transformation for the specified multimarker. After * a call to detectMarker, all marker information will be current. Marker transformations can then be * checked. * * @param {number} markerUID The unique identifier (UID) of the marker to query * @param {Float64Array} dst The float array to populate with the 3x4 marker transformation matrix * @return {Float64Array} The dst array. */ public getTransMatMultiSquare(markerUID: number, dst: Float64Array) { ARToolKit.getTransMatMultiSquare(this.id, markerUID); dst.set(this.markerTransformMat); return dst; } /** * Populates the provided float array with the current robust transformation for the specified multimarker. After * a call to detectMarker, all marker information will be current. Marker transformations can then be * checked. * @param {number} markerUID The unique identifier (UID) of the marker to query * @param {Float64Array} dst The float array to populate with the 3x4 marker transformation matrix * @return {Float64Array} The dst array. */ public getTransMatMultiSquareRobust(markerUID: number, dst: Float64Array) { ARToolKit.getTransMatMultiSquare(this.id, markerUID); dst.set(this.markerTransformMat); return dst; } /** * Converts the given 3x4 marker transformation matrix in the 12-element transMat array * into a 4x4 WebGL matrix and writes the result into the 16-element glMat array. * If scale parameter is given, scales the transform of the glMat by the scale parameter. * @param {Float64Array} transMat The 3x4 marker transformation matrix. * @param {Float64Array} glMat The 4x4 GL transformation matrix. * @param {number} [scale] The scale for the transform. */ public transMatToGLMat( transMat: Float64Array, glMat: Float64Array, scale?: number ) { glMat[0 + 0 * 4] = transMat[0]; // R1C1 glMat[0 + 1 * 4] = transMat[1]; // R1C2 glMat[0 + 2 * 4] = transMat[2]; glMat[0 + 3 * 4] = transMat[3]; glMat[1 + 0 * 4] = transMat[4]; // R2 glMat[1 + 1 * 4] = transMat[5]; glMat[1 + 2 * 4] = transMat[6]; glMat[1 + 3 * 4] = transMat[7]; glMat[2 + 0 * 4] = transMat[8]; // R3 glMat[2 + 1 * 4] = transMat[9]; glMat[2 + 2 * 4] = transMat[10]; glMat[2 + 3 * 4] = transMat[11]; glMat[3 + 0 * 4] = 0.0; glMat[3 + 1 * 4] = 0.0; glMat[3 + 2 * 4] = 0.0; glMat[3 + 3 * 4] = 1.0; if (scale !== undefined && scale !== 0.0) { glMat[12] *= scale; glMat[13] *= scale; glMat[14] *= scale; } return glMat; } /** * This is the core ARToolKit marker detection function. It calls through to a set of * internal functions to perform the key marker detection steps of binarization and * labelling, contour extraction, and template matching and/or matrix code extraction. * Typically, the resulting set of detected markers is retrieved by calling arGetMarkerNum * to get the number of markers detected and arGetMarker to get an array of ARMarkerInfo * structures with information on each detected marker, followed by a step in which * detected markers are possibly examined for some measure of goodness of match (e.g. by * examining the match confidence value) and pose extraction. * @param {HTMLImageElement|HTMLVideoElement} [image] to be processed to detect markers. * @return {number} 0 if the function proceeded without error, or a value less than 0 in case of error. * A result of 0 does not however, imply any markers were detected. */ public detectMarker(imageElement: HTMLImageElement | HTMLVideoElement) { if (this._copyImageToHeap(imageElement)) { return ARToolKit.detectMarker(this.id); } return -99; } /** * Get the number of markers detected in a video frame. * @return {number} The number of detected markers in the most recent image passed to arDetectMarker. * Note that this is actually a count, not an index. A better name for this function would be * arGetDetectedMarkerCount, but the current name lives on for historical reasons. */ public getMarkerNum() { return ARToolKit.getMarkerNum(this.id); } /** * Get the marker info struct for the given marker index in detected markers. * Call this.detectMarker first, then use this.getMarkerNum to get the detected marker count. * The returned object is the global artoolkit.markerInfo object and will be overwritten * by subsequent calls. If you need to hang on to it, create a copy using this.cloneMarkerInfo(); * Returns undefined if no marker was found. * A markerIndex of -1 is used to access the global custom marker. * The fields of the markerInfo struct are: * @field area Area in pixels of the largest connected region, comprising * the marker border and regions connected to it. Note that this is * not the same as the actual onscreen area inside the marker border. * @field id If pattern detection mode is either pattern mode OR * matrix but not both, will be marker ID (>= 0) if marker is valid, or -1 if invalid. * @field idPatt If pattern detection mode includes a pattern mode, * will be marker ID (>= 0) if marker is valid, or -1 if invalid. * @field idMatrix If pattern detection mode includes a matrix mode, * will be marker ID (>= 0) if marker is valid, or -1 if invalid. * @field dir If pattern detection mode is either pattern mode OR matrix * but not both, and id != -1, will be marker direction (range 0 to 3, inclusive). * @field dirPatt If pattern detection mode includes a pattern mode, and * id != -1, will be marker direction (range 0 to 3, inclusive). * @field dirMatrix If pattern detection mode includes a matrix mode, * and id != -1, will be marker direction (range 0 to 3, inclusive). * @field cf If pattern detection mode is either pattern mode OR * matrix but not both, will be marker matching confidence (range 0.0 to 1.0 inclusive) * if marker is valid, or -1.0 if marker is invalid. * @field cfPatt If pattern detection mode includes a pattern mode, * will be marker matching confidence (range 0.0 to 1.0 inclusive) if marker is valid, or -1.0 if marker is invalid. * @field cfMatrix If pattern detection mode includes a matrix mode, * will be marker matching confidence (range 0.0 to 1.0 inclusive) if marker is valid, or -1.0 if marker is invalid. * @field pos 2D position (in camera image coordinates, origin at top-left) * of the centre of the marker. * @field line Line equations for the 4 sides of the marker. * @field vertex 2D positions (in camera image coordinates, origin at top-left) * of the corners of the marker. vertex[(4 - dir)%4][] is the top-left corner of the marker. * Other vertices proceed clockwise from this. These are idealised coordinates * (i.e. the onscreen position aligns correctly with the undistorted camera image.) * @param {number} markerIndex The index of the marker to query. * @returns {Object} The markerInfo struct. */ public getMarker(markerIndex: number) { if (0 === ARToolKit.getMarker(this.id, markerIndex)) { return ARToolKit.getMarkerInfo(); } } /** * Set marker vertices to the given vertexData[4][2] array. * Sets the marker pos to the center of the vertices. * Useful for building custom markers for getTransMatSquare. * A markerIndex of -1 is used to access the global custom marker. * @param {number} markerIndex The index of the marker to edit. * @param {*} vertexData */ public setMarkerInfoVertex(markerIndex: number, vertexData: any) { for (let i = 0; i < vertexData.length; i++) { this.markerTransformMat[i * 2 + 0] = vertexData[i][0]; this.markerTransformMat[i * 2 + 1] = vertexData[i][1]; } return ARToolKit.setMarkerInfoVertex(this.id, markerIndex); } /** * Makes a deep copy of the given marker info. * @param {Object} markerInfo The marker info object to copy. * @return {Object} The new copy of the marker info. */ public cloneMarkerInfo(markerInfo: any) { return JSON.parse(JSON.stringify(markerInfo)); } /** * Get the marker info struct for the given marker index in detected markers. * Call this.detectMarker first, then use this.getMarkerNum to get the detected marker count. * The returned object is the global artoolkit.markerInfo object and will be overwritten * by subsequent calls. If you need to hang on to it, create a copy using this.cloneMarkerInfo(); * Returns undefined if no marker was found. * @field {number} pattId The index of the marker. * @field {number} pattType The type of the marker. * Either AR_MULTI_PATTERN_TYPE_TEMPLATE or AR_MULTI_PATTERN_TYPE_MATRIX. * @field {number} visible 0 or larger if the marker is visible * @field {number} width The width of the marker. * @param {number} multiMarkerId The multimarker to query. * @param {number} markerIndex The index of the marker to query. * @returns {Object} The markerInfo struct. */ public getMultiEachMarker(multiMarkerId: number, markerIndex: number) { if ( 0 === ARToolKit.getMultiEachMarker(this.id, multiMarkerId, markerIndex) ) { return ARToolKit.multiEachMarkerInfo; } } /** * Returns the 16-element WebGL transformation matrix used by ARController.process to * pass marker WebGL matrices to event listeners. * Unique to each ARController. * @return {Float64Array} The 16-element WebGL transformation matrix used by the ARController. */ public getTransformationMatrix() { return this.transformMat; } /** * Returns the projection matrix computed from camera parameters for the ARController. * * @return {Float64Array} The 16-element WebGL camera matrix for the ARController camera parameters. */ public getCameraMatrix(): Float64Array { return this.cameraMat; } /** * Returns the shared ARToolKit 3x4 marker transformation matrix, used for passing and receiving * marker transforms to/from the Emscripten side. * @return {Float64Array} The 12-element 3x4 row-major marker transformation matrix used by ARToolKit. */ public getMarkerTransformationMatrix(): Float64Array { return this.markerTransformMat; } /* Setter / Getter Proxies */ /** * Enables or disables debug mode in the tracker. When enabled, a black and white debug * image is generated during marker detection. The debug image is useful for visualising * the binarization process and choosing a threshold value. * @param {boolean} mode true to enable debug mode, false to disable debug mode * @see getDebugMode() */ public setDebugMode(mode: number) { return ARToolKit.setDebugMode(this.id, mode); } /** * Returns whether debug mode is currently enabled. * @return {boolean} true when debug mode is enabled, false when debug mode is disabled * @see setDebugMode() */ public getDebugMode(): boolean { return ARToolKit.getDebugMode(this.id); } /** * Returns the Emscripten HEAP offset to the debug processing image used by ARToolKit. * * @return {number} HEAP offset to the debug processing image. */ public getProcessingImage() { return ARToolKit.getProcessingImage(this.id); } /** * Sets the logging level to use by ARToolKit. * * //TODOC * @param mode */ public setLogLevel(mode: any) { return ARToolKit.setLogLevel(mode); } /** * //TODOC * @returns {*} */ public getLogLevel() { return ARToolKit.getLogLevel(); } /** * //TODOC * @param markerIndex * @param dir * @returns {*} */ public setMarkerInfoDir(markerIndex: number, dir: any) { return ARToolKit.setMarkerInfoDir(this.id, markerIndex, dir); } /** * //TODOC * @param value * @returns {*} */ public setProjectionNearPlane(value: any) { return ARToolKit.setProjectionNearPlane(this.id, value); } /** * //TODOC * @returns {*} */ public getProjectionNearPlane() { return ARToolKit.getProjectionNearPlane(this.id); } /** * //TODOC * @param value * @returns {*} */ public setProjectionFarPlane(value: any) { return ARToolKit.setProjectionFarPlane(this.id, value); } /** * //TODOC * @returns {*} */ public getProjectionFarPlane() { return ARToolKit.getProjectionFarPlane(this.id); } /** * Set the labeling threshold mode (auto/manual). * @param {number} mode An integer specifying the mode. One of: * AR_LABELING_THRESH_MODE_MANUAL, * AR_LABELING_THRESH_MODE_AUTO_MEDIAN, * AR_LABELING_THRESH_MODE_AUTO_OTSU, * AR_LABELING_THRESH_MODE_AUTO_ADAPTIVE, * AR_LABELING_THRESH_MODE_AUTO_BRACKETING */ public setThresholdMode(mode: any) { return ARToolKit.setThresholdMode(this.id, mode); } /** * Gets the current threshold mode used for image binarization. * @return {number} The current threshold mode * @see getVideoThresholdMode() */ public getThresholdMode() { return ARToolKit.getThresholdMode(this.id); } /** * Set the labeling threshhold. * This function forces sets the threshold value. * The default value is AR_DEFAULT_LABELING_THRESH which is 100. * The current threshold mode is not affected by this call. * Typically, this function is used when labeling threshold mode * is AR_LABELING_THRESH_MODE_MANUAL. * The threshold value is not relevant if threshold mode is * AR_LABELING_THRESH_MODE_AUTO_ADAPTIVE. * * Background: The labeling threshold is the value which * the AR library uses to differentiate between black and white * portions of an ARToolKit marker. Since the actual brightness, * contrast, and gamma of incoming images can vary signficantly * between different cameras and lighting conditions, this * value typically needs to be adjusted dynamically to a * suitable midpoint between the observed values for black * and white portions of the markers in the image. * * @param {number} threshold An integer in the range [0,255] (inclusive). */ public setThreshold(threshold: number) { return ARToolKit.setThreshold(this.id, threshold); } /** * Get the current labeling threshold. * * This function queries the current labeling threshold. For, * AR_LABELING_THRESH_MODE_AUTO_MEDIAN, AR_LABELING_THRESH_MODE_AUTO_OTSU, * and AR_LABELING_THRESH_MODE_AUTO_BRACKETING * the threshold value is only valid until the next auto-update. * * The current threshold mode is not affected by this call. * * The threshold value is not relevant if threshold mode is * AR_LABELING_THRESH_MODE_AUTO_ADAPTIVE. * * @return {number} The current threshold value. */ public getThreshold() { return ARToolKit.getThreshold(this.id); } /** * Set the pattern detection mode * * The pattern detection determines the method by which ARToolKit * matches detected squares in the video image to marker templates * and/or IDs. ARToolKit v4.x can match against pictorial "template" markers, * whose pattern files are created with the mk_patt utility, in either colour * or mono, and additionally can match against 2D-barcode-type "matrix" * markers, which have an embedded marker ID. Two different two-pass modes * are also available, in which a matrix-detection pass is made first, * followed by a template-matching pass. * * @param {number} mode * Options for this field are: * AR_TEMPLATE_MATCHING_COLOR * AR_TEMPLATE_MATCHING_MONO * AR_MATRIX_CODE_DETECTION * AR_TEMPLATE_MATCHING_COLOR_AND_MATRIX * AR_TEMPLATE_MATCHING_MONO_AND_MATRIX * The default mode is AR_TEMPLATE_MATCHING_COLOR. */ public setPatternDetectionMode(mode: number) { return ARToolKit.setPatternDetectionMode(this.id, mode); } /** * Returns the current pattern detection mode. * @return {number} The current pattern detection mode. */ public getPatternDetectionMode() { return ARToolKit.getPatternDetectionMode(this.id); } /** * Set the size and ECC algorithm to be used for matrix code (2D barcode) marker detection. * * When matrix-code (2D barcode) marker detection is enabled (see arSetPatternDetectionMode) * then the size of the barcode pattern and the type of error checking and correction (ECC) * with which the markers were produced can be set via this function. * * This setting is global to a given ARHandle; It is not possible to have two different matrix * code types in use at once. * * @param type The type of matrix code (2D barcode) in use. Options include: * AR_MATRIX_CODE_3x3 * AR_MATRIX_CODE_3x3_HAMMING63 * AR_MATRIX_CODE_3x3_PARITY65 * AR_MATRIX_CODE_4x4 * AR_MATRIX_CODE_4x4_BCH_13_9_3 * AR_MATRIX_CODE_4x4_BCH_13_5_5 * The default mode is AR_MATRIX_CODE_3x3. */ public setMatrixCodeType(type: any) { return ARToolKit.setMatrixCodeType(this.id, type); } /** * Returns the current matrix code (2D barcode) marker detection type. * @return {number} The current matrix code type. */ public getMatrixCodeType() { return ARToolKit.getMatrixCodeType(this.id); } /** * Select between detection of black markers and white markers. * * ARToolKit's labelling algorithm can work with both black-bordered * markers on a white background (AR_LABELING_BLACK_REGION) or * white-bordered markers on a black background (AR_LABELING_WHITE_REGION). * This function allows you to specify the type of markers to look for. * Note that this does not affect the pattern-detection algorith * which works on the interior of the marker. * * @param {number} mode * Options for this field are: * AR_LABELING_WHITE_REGION * AR_LABELING_BLACK_REGION * The default mode is AR_LABELING_BLACK_REGION. */ public setLabelingMode(mode: any) { return ARToolKit.setLabelingMode(this.id, mode); } /** * Enquire whether detection is looking for black markers or white markers. * See discussion for setLabelingMode. * @result {number} The current labeling mode. */ public getLabelingMode() { return ARToolKit.getLabelingMode(this.id); } /** * Set the width/height of the marker pattern space, as a proportion of marker width/height. * @param {number} pattRatio The the width/height of the marker pattern space, as a proportion of marker * width/height. To set the default, pass AR_PATT_RATIO. * If compatibility with ARToolKit verions 1.0 through 4.4 is required, this value * must be 0.5. */ public setPattRatio(pattRatio: number) { return ARToolKit.setPattRatio(this.id, pattRatio); } /** * Returns the current ratio of the marker pattern to the total marker size. * @return {number} The current pattern ratio. */ public getPattRatio(): number { return ARToolKit.getPattRatio(this.id); } /** * Set the image processing mode. * * When the image processing mode is AR_IMAGE_PROC_FRAME_IMAGE, * ARToolKit processes all pixels in each incoming image * to locate markers. When the mode is AR_IMAGE_PROC_FIELD_IMAGE, * ARToolKit processes pixels in only every second pixel row and * column. This is useful both for handling images from interlaced * video sources (where alternate lines are assembled from alternate * fields and thus have one field time-difference, resulting in a * "comb" effect) such as Digital Video cameras. * The effective reduction by 75% in the pixels processed also * has utility in accelerating tracking by effectively reducing * the image size to one quarter size, at the cost of pose accuraccy. * * @param {number} mode * Options for this field are: * AR_IMAGE_PROC_FRAME_IMAGE * AR_IMAGE_PROC_FIELD_IMAGE * The default mode is AR_IMAGE_PROC_FRAME_IMAGE. */ public setImageProcMode(mode: any) { return ARToolKit.setImageProcMode(this.id, mode); } /** * Get the image processing mode. * See arSetImageProcMode() for a complete description. * @return {number} The current image processing mode. */ public getImageProcMode() { return ARToolKit.getImageProcMode(this.id); } /** * Draw the black and white image and debug markers to the ARController canvas. * See setDebugMode. */ public debugDraw() { const debugBuffer = new Uint8ClampedArray( ARToolKit.HEAPU8.buffer, this._bwpointer, this.framesize ); const id = new ImageData( debugBuffer, this.canvas.width, this.canvas.height ); if (this.ctx) { this.ctx.putImageData(id, 0, 0); } else { throw Error(this.contextError); } const marker_num = this.getMarkerNum(); for (let i = 0; i < marker_num; i++) { this._debugMarker(this.getMarker(i)); } } private _initialize() { this.id = ARToolKit.setup( this.canvas.width, this.canvas.height, this.cameraParam.id ); const params = ARToolKit.getFrameMalloc(); if (params) { this.framepointer = params.framepointer; this.framesize = params.framesize; this.dataHeap = new Uint8Array( ARToolKit.HEAPU8.buffer, this.framepointer, this.framesize ); this.cameraMat = new Float64Array( ARToolKit.HEAPU8.buffer, params.camera, 16 ); this.markerTransformMat = new Float64Array( ARToolKit.HEAPU8.buffer, params.transform, 12 ); } else { throw Error("frameMalloc not set"); } this.setProjectionNearPlane(0.1); this.setProjectionFarPlane(1000); setTimeout(() => { if (this.onload) { this.onload(); } this.dispatchEvent({ name: "load", target: this }); }, 0); } /** * //TODOC * * @param {HTMLImageElement|HTMLVideoElement} [image] * @returns {boolean} * @private */ private _copyImageToHeap(image: HTMLImageElement | HTMLVideoElement) { if (this.ctx === null) { throw Error(this.contextError); } if (!image) { image = this.image; } if ( (image instanceof HTMLImageElement && image.width > image.height) || (image instanceof HTMLVideoElement && image.videoWidth > image.videoHeight) ) { // if landscape this.ctx.drawImage(image, 0, 0, this.canvas.width, this.canvas.height); // draw video } else { // if portrait this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); const scale = this.canvas.height / this.canvas.width; const scaledHeight = this.canvas.width * scale; const scaledWidth = this.canvas.height * scale; const marginLeft = (this.canvas.width - scaledWidth) / 2; this.ctx.drawImage(image, marginLeft, 0, scaledWidth, scaledHeight); // draw video } const imageData = this.ctx.getImageData( 0, 0, this.canvas.width, this.canvas.height ); const data = imageData.data; if (this.dataHeap) { this.dataHeap.set(data); return true; } return false; } /** * //TODOC * * @param marker * @private */ private _debugMarker(marker: any) { if (this.ctx === null) { throw Error(this.contextError); } let vertex; let pos; vertex = marker.vertex; const ctx = this.ctx; ctx.strokeStyle = "red"; ctx.beginPath(); ctx.moveTo(vertex[0][0], vertex[0][1]); ctx.lineTo(vertex[1][0], vertex[1][1]); ctx.stroke(); ctx.beginPath(); ctx.moveTo(vertex[2][0], vertex[2][1]); ctx.lineTo(vertex[3][0], vertex[3][1]); ctx.stroke(); ctx.strokeStyle = "green"; ctx.beginPath(); ctx.lineTo(vertex[1][0], vertex[1][1]); ctx.lineTo(vertex[2][0], vertex[2][1]); ctx.stroke(); ctx.beginPath(); ctx.moveTo(vertex[3][0], vertex[3][1]); ctx.lineTo(vertex[0][0], vertex[0][1]); ctx.stroke(); pos = marker.pos; ctx.beginPath(); ctx.arc(pos[0], pos[1], 8, 0, Math.PI * 2); ctx.fillStyle = "red"; ctx.fill(); } } export default ARToolKitController;