提交 e09bba26 编写于 作者: J Jerome Etienne

mopre work

上级 770eb8d6
# GOAL
- filtering : convert2Grey + adaptativeThresholding
- code this in multiple versions : webworkers - webassembly - gpu
- mix them together - which combinasion
- Bench how fast the web can go about image processing
- Example on filtering : convert2Grey + adaptativeThresholding
- Code this in multiple versions : webworkers - webassembly - gpu
- Then mix them together - determine which combinason is best
# Step1 Basic Demo
# Step1 Basic Demo - onecore-purejs
- read the webcam
- display the origianl image
- filter the image - use jsaruco function
- display the filtered image
# Step2 Implement webworkers + jsaruco
# Step2 Implement webworkers + jsaruco - multicore-purejs
- aka all normal javascript - no webassembly so more stable
# Step3 Implement webassembly
# Step3 Implement webassembly - onecore-wasm
- code in C the convert2Grey yourself first
- see about getting a horintal/vertical blur in C
- then do a adaptative thresholding
......
Found issues
- jsaruco use a kernel size of 2 in adaptative thresholding
- could i reduce the resolution of the source image and use a kernel size of 1 ?
- it would produce more fps. what the difference would be ? create errors ?
- jsaruco - adaptiveThreshold is doing it on ALL bytes - so all channel ???
- it use blackwhite image - it only needs 1 channel - 8 bits is already a lot to store blackwhite
- this mean 4 times more work than needed
- NOTES: unclear this is true - grayscale is packing it all in 1 channel. check it out ?
# API issue
# Browser API issue
- missing videoElement.getImageData(). It is only available in canvas context
- cause a canvasContext.drawImage(videoElement) + canvasContext.getImageData()
- missing getImageData() .data directly as SharedArrayBuffer
......
......@@ -6,3 +6,4 @@
- worker - telling main-thread the process is completed
- issue with the setup
- i copy the whole thing SO SO much
- can i copy the frame less often
......@@ -17,8 +17,6 @@
var imageArray = new Uint8Array(imageBuffer);
imageArray.set(srcImageData.data)
var videoElement = document.createElement('video')
// document.body.appendChild(videoElement)
videoElement.autoplay = true
......
- Check js implementation and their multi channels issue
- tried with prepacked
- try with webassembly
- try with webworker
- how to use webworker ?
- split the processing of the original image
- so gray scale + adatative threshold - splitted in 4 zones for 4 cpu
- https://github.com/andrei-markeev/ts2c
---
# webworker usage
- in 640x480, it take 18.3ms for context.detect
- 13.84ms for greyscale + adaptative threshold - 75% of the whole!
- it is doable on GPU too
- greyscale is per pixel
- adaptative threshold needs a local average blur and then it is per pixel
- there is a pool of workers
- and add task to it
- doing the greyscale
- doing the horizontal blur
- doing the vertical blur
- demo where you read the webcam and filter it with this
- expose all the hardcoded parameters from AR.Detect in the debug,html
- thus you can tune them
---
- see how to include it in ar.js
- so basically a context and a controls
- the source can remain the same without trouble
- how to handle all the options between the various backend
# Performances
- jsaruco use a kernel size of 2 in adaptative thresholding
- could i reduce the resolution of the source image and use a kernel size of 1 ?
- it would produce more fps. what the difference would be ? create errors ?
- jsaruco - adaptiveThreshold is doing it on ALL bytes - so all channel ??? check if it is correct
- it use blackwhite image - it only needs 1 channel - 8 bits is already a lot to store blackwhite
- this mean 4 times more work than needed
- NOTES: unclear this is true - grayscale is packing it all in 1 channel. check it out ?
- in posit1: image difference is computed by a manathan distance. not a euclidian distance... how come ?
- imageDifference += Math.abs(sopImagePoints[i].x - oldSopImagePoints[i].x);
imageDifference += Math.abs(sopImagePoints[i].y - oldSopImagePoints[i].y);
......@@ -166,14 +166,16 @@
<style media="screen">
img {
width: 256px;
height: 256px;
width: 128px;
height: 128px;
padding: 8em;
}
</style>
<br/>
<div>
<!-- <img src="images/1001.png"/> -->
<img src="images/1001.png"/>
<img src="images/1001.png"/>
<img src="images/1001.png"/>
<img src="images/1001.png"/>
</div>
</body>
......
......@@ -13,10 +13,7 @@
<!-- <script src='../build/threex-aruco-prepacked.js'></script> -->
<body style='background-color: darkgray;'>
<div id='webGLContainer' style='display: inline;'></div>
<script>
......@@ -134,8 +131,8 @@
<style media="screen">
img {
width: 256px;
height: 256px;
width: 128px;
height: 128px;
padding: 8em;
}
</style>
......
......@@ -2,8 +2,8 @@ var THREEx = THREEx || {}
THREEx.ArucoContext = function(markerSize){
this.canvas = document.createElement('canvas');
this.canvas.width = 80*8
this.canvas.height = 60*8
this.canvas.width = 80*4
this.canvas.height = 60*4
// experiment with imageSmoothingEnabled
var imageSmoothingEnabled = false
......@@ -30,8 +30,8 @@ THREEx.ArucoContext.prototype.detect = function (videoElement) {
var detectedMarkers = this.detector.detect(imageData);
// compute the pose for each detectedMarkers
// FIXME: i fix we should have one posit estimator per marker
detectedMarkers.forEach(function(detectedMarker){
// debugger
var markerCorners = detectedMarker.corners;
// convert the corners
......
......@@ -23,245 +23,246 @@ THE SOFTWARE.
/*
References:
- "ArUco: a minimal library for Augmented Reality applications based on OpenCv"
http://www.uco.es/investiga/grupos/ava/node/26
http://www.uco.es/investiga/grupos/ava/node/26
*/
var AR = AR || {};
AR.Marker = function(id, corners){
this.id = id;
this.corners = corners;
this.id = id;
this.corners = corners;
};
AR.Detector = function(){
this.grey = new CV.Image();
this.thres = new CV.Image();
this.homography = new CV.Image();
this.binary = [];
this.contours = [];
this.polys = [];
this.candidates = [];
this.grey = new CV.Image();
this.thres = new CV.Image();
this.homography = new CV.Image();
this.binary = [];
this.contours = [];
this.polys = [];
this.candidates = [];
};
AR.Detector.prototype.detect = function(image){
CV.grayscale(image, this.grey);
CV.adaptiveThreshold(this.grey, this.thres, 2, 7);
this.contours = CV.findContours(this.thres, this.binary);
this.candidates = this.findCandidates(this.contours, image.width * 0.20, 0.05, 10);
this.candidates = this.clockwiseCorners(this.candidates);
this.candidates = this.notTooNear(this.candidates, 10);
return this.findMarkers(this.grey, this.candidates, 49);
CV.grayscale(image, this.grey);
CV.adaptiveThreshold(this.grey, this.thres, 2, 7);
this.contours = CV.findContours(this.thres, this.binary);
this.candidates = this.findCandidates(this.contours, image.width * 0.20, 0.05, 10);
this.candidates = this.clockwiseCorners(this.candidates);
this.candidates = this.notTooNear(this.candidates, 10);
return this.findMarkers(this.grey, this.candidates, 49);
};
AR.Detector.prototype.findCandidates = function(contours, minSize, epsilon, minLength){
var candidates = [], len = contours.length, contour, poly, i;
this.polys = [];
for (i = 0; i < len; ++ i){
contour = contours[i];
if (contour.length >= minSize){
poly = CV.approxPolyDP(contour, contour.length * epsilon);
this.polys.push(poly);
if ( (4 === poly.length) && ( CV.isContourConvex(poly) ) ){
if ( CV.minEdgeLength(poly) >= minLength){
candidates.push(poly);
var candidates = [], len = contours.length, contour, poly, i;
this.polys = [];
for (i = 0; i < len; ++ i){
contour = contours[i];
if (contour.length >= minSize){
poly = CV.approxPolyDP(contour, contour.length * epsilon);
this.polys.push(poly);
if ( (4 === poly.length) && ( CV.isContourConvex(poly) ) ){
if ( CV.minEdgeLength(poly) >= minLength){
candidates.push(poly);
}
}
}
}
}
}
}
return candidates;
return candidates;
};
AR.Detector.prototype.clockwiseCorners = function(candidates){
var len = candidates.length, dx1, dx2, dy1, dy2, swap, i;
for (i = 0; i < len; ++ i){
dx1 = candidates[i][1].x - candidates[i][0].x;
dy1 = candidates[i][1].y - candidates[i][0].y;
dx2 = candidates[i][2].x - candidates[i][0].x;
dy2 = candidates[i][2].y - candidates[i][0].y;
if ( (dx1 * dy2 - dy1 * dx2) < 0){
swap = candidates[i][1];
candidates[i][1] = candidates[i][3];
candidates[i][3] = swap;
}
}
return candidates;
var len = candidates.length, dx1, dx2, dy1, dy2, swap, i;
for (i = 0; i < len; ++ i){
dx1 = candidates[i][1].x - candidates[i][0].x;
dy1 = candidates[i][1].y - candidates[i][0].y;
dx2 = candidates[i][2].x - candidates[i][0].x;
dy2 = candidates[i][2].y - candidates[i][0].y;
if ( (dx1 * dy2 - dy1 * dx2) < 0){
swap = candidates[i][1];
candidates[i][1] = candidates[i][3];
candidates[i][3] = swap;
}
}
return candidates;
};
AR.Detector.prototype.notTooNear = function(candidates, minDist){
var notTooNear = [], len = candidates.length, dist, dx, dy, i, j, k;
for (i = 0; i < len; ++ i){
for (j = i + 1; j < len; ++ j){
dist = 0;
for (k = 0; k < 4; ++ k){
dx = candidates[i][k].x - candidates[j][k].x;
dy = candidates[i][k].y - candidates[j][k].y;
dist += dx * dx + dy * dy;
}
if ( (dist / 4) < (minDist * minDist) ){
if ( CV.perimeter( candidates[i] ) < CV.perimeter( candidates[j] ) ){
candidates[i].tooNear = true;
}else{
candidates[j].tooNear = true;
var notTooNear = [], len = candidates.length, dist, dx, dy, i, j, k;
for (i = 0; i < len; ++ i){
for (j = i + 1; j < len; ++ j){
dist = 0;
for (k = 0; k < 4; ++ k){
dx = candidates[i][k].x - candidates[j][k].x;
dy = candidates[i][k].y - candidates[j][k].y;
dist += dx * dx + dy * dy;
}
if ( (dist / 4) < (minDist * minDist) ){
if ( CV.perimeter( candidates[i] ) < CV.perimeter( candidates[j] ) ){
candidates[i].tooNear = true;
}else{
candidates[j].tooNear = true;
}
}
}
}
}
}
}
for (i = 0; i < len; ++ i){
if ( !candidates[i].tooNear ){
notTooNear.push( candidates[i] );
}
}
return notTooNear;
for (i = 0; i < len; ++ i){
if ( !candidates[i].tooNear ){
notTooNear.push( candidates[i] );
}
}
return notTooNear;
};
AR.Detector.prototype.findMarkers = function(imageSrc, candidates, warpSize){
var markers = [], len = candidates.length, candidate, marker, i;
for (i = 0; i < len; ++ i){
candidate = candidates[i];
CV.warp(imageSrc, this.homography, candidate, warpSize);
CV.threshold(this.homography, this.homography, CV.otsu(this.homography) );
marker = this.getMarker(this.homography, candidate);
if (marker){
markers.push(marker);
}
}
return markers;
var markers = [], len = candidates.length, candidate, marker, i;
for (i = 0; i < len; ++ i){
candidate = candidates[i];
CV.warp(imageSrc, this.homography, candidate, warpSize);
CV.threshold(this.homography, this.homography, CV.otsu(this.homography) );
marker = this.getMarker(this.homography, candidate);
if (marker){
markers.push(marker);
}
}
return markers;
};
AR.Detector.prototype.getMarker = function(imageSrc, candidate){
var width = (imageSrc.width / 7) >>> 0,
minZero = (width * width) >> 1,
bits = [], rotations = [], distances = [],
square, pair, inc, i, j;
for (i = 0; i < 7; ++ i){
inc = (0 === i || 6 === i)? 1: 6;
for (j = 0; j < 7; j += inc){
square = {x: j * width, y: i * width, width: width, height: width};
if ( CV.countNonZero(imageSrc, square) > minZero){
return null;
}
}
}
for (i = 0; i < 5; ++ i){
bits[i] = [];
for (j = 0; j < 5; ++ j){
square = {x: (j + 1) * width, y: (i + 1) * width, width: width, height: width};
bits[i][j] = CV.countNonZero(imageSrc, square) > minZero? 1: 0;
}
}
rotations[0] = bits;
distances[0] = this.hammingDistance( rotations[0] );
pair = {first: distances[0], second: 0};
for (i = 1; i < 4; ++ i){
rotations[i] = this.rotate( rotations[i - 1] );
distances[i] = this.hammingDistance( rotations[i] );
if (distances[i] < pair.first){
pair.first = distances[i];
pair.second = i;
}
}
if (0 !== pair.first){
return null;
}
return new AR.Marker(
this.mat2id( rotations[pair.second] ),
this.rotate2(candidate, 4 - pair.second) );
var width = (imageSrc.width / 7) >>> 0,
minZero = (width * width) >> 1,
bits = [], rotations = [], distances = [],
square, pair, inc, i, j;
for (i = 0; i < 7; ++ i){
inc = (0 === i || 6 === i)? 1: 6;
for (j = 0; j < 7; j += inc){
square = {x: j * width, y: i * width, width: width, height: width};
if ( CV.countNonZero(imageSrc, square) > minZero){
return null;
}
}
}
for (i = 0; i < 5; ++ i){
bits[i] = [];
for (j = 0; j < 5; ++ j){
square = {x: (j + 1) * width, y: (i + 1) * width, width: width, height: width};
bits[i][j] = CV.countNonZero(imageSrc, square) > minZero? 1: 0;
}
}
rotations[0] = bits;
distances[0] = this.hammingDistance( rotations[0] );
pair = {first: distances[0], second: 0};
for (i = 1; i < 4; ++ i){
rotations[i] = this.rotate( rotations[i - 1] );
distances[i] = this.hammingDistance( rotations[i] );
if (distances[i] < pair.first){
pair.first = distances[i];
pair.second = i;
}
}
if (0 !== pair.first){
return null;
}
return new AR.Marker(
this.mat2id( rotations[pair.second] ),
this.rotate2(candidate, 4 - pair.second)
);
};
AR.Detector.prototype.hammingDistance = function(bits){
var ids = [ [1,0,0,0,0], [1,0,1,1,1], [0,1,0,0,1], [0,1,1,1,0] ],
dist = 0, sum, minSum, i, j, k;
for (i = 0; i < 5; ++ i){
minSum = Infinity;
for (j = 0; j < 4; ++ j){
sum = 0;
for (k = 0; k < 5; ++ k){
sum += bits[i][k] === ids[j][k]? 0: 1;
}
if (sum < minSum){
minSum = sum;
}
}
dist += minSum;
}
return dist;
var ids = [ [1,0,0,0,0], [1,0,1,1,1], [0,1,0,0,1], [0,1,1,1,0] ],
dist = 0, sum, minSum, i, j, k;
for (i = 0; i < 5; ++ i){
minSum = Infinity;
for (j = 0; j < 4; ++ j){
sum = 0;
for (k = 0; k < 5; ++ k){
sum += bits[i][k] === ids[j][k]? 0: 1;
}
if (sum < minSum){
minSum = sum;
}
}
dist += minSum;
}
return dist;
};
AR.Detector.prototype.mat2id = function(bits){
var id = 0, i;
for (i = 0; i < 5; ++ i){
id <<= 1;
id |= bits[i][1];
id <<= 1;
id |= bits[i][3];
}
return id;
var id = 0, i;
for (i = 0; i < 5; ++ i){
id <<= 1;
id |= bits[i][1];
id <<= 1;
id |= bits[i][3];
}
return id;
};
AR.Detector.prototype.rotate = function(src){
var dst = [], len = src.length, i, j;
for (i = 0; i < len; ++ i){
dst[i] = [];
for (j = 0; j < src[i].length; ++ j){
dst[i][j] = src[src[i].length - j - 1][i];
}
}
return dst;
var dst = [], len = src.length, i, j;
for (i = 0; i < len; ++ i){
dst[i] = [];
for (j = 0; j < src[i].length; ++ j){
dst[i][j] = src[src[i].length - j - 1][i];
}
}
return dst;
};
AR.Detector.prototype.rotate2 = function(src, rotation){
var dst = [], len = src.length, i;
for (i = 0; i < len; ++ i){
dst[i] = src[ (rotation + i) % len ];
}
return dst;
var dst = [], len = src.length, i;
for (i = 0; i < len; ++ i){
dst[i] = src[ (rotation + i) % len ];
}
return dst;
};
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册