kopia lustrzana https://github.com/halfmonty/StringArtGenerator
init
commit
9bebcaf803
|
@ -0,0 +1,392 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/gh/nicolaspanel/numjs@0.15.1/dist/numjs.min.js"></script>
|
||||
<script src="ndarray.js"></script>
|
||||
<script src="loading-bar.js"></script>
|
||||
<title>Hello OpenCV.js</title>
|
||||
<link rel="stylesheet" type="text/css" href="loading-bar.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h2>Hello OpenCV.js</h2>
|
||||
<p id="status">OpenCV.js is loading...</p>
|
||||
<div id="loadingbar"></div><br/><br/>
|
||||
<div>
|
||||
<div class="inputoutput">
|
||||
<div class="caption">imageSrc <input type="file" id="fileInput" name="file" /></div><br/>
|
||||
<img id="imageSrc" alt="No Image" />
|
||||
</div>
|
||||
<div class="inputoutput">
|
||||
<div class="caption">Temp Formatted output</div>
|
||||
<canvas id="canvasOutput" ></canvas>
|
||||
</div>
|
||||
<div class="inputoutput">
|
||||
<div class="caption">Line Output</div>
|
||||
<canvas id="canvasOutput2" ></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
let MAX_LINES = 4000;
|
||||
let N_PINS = 36*8;
|
||||
let MIN_LOOP = 20;
|
||||
let MIN_DISTANCE = 20;
|
||||
let LINE_WEIGHT = 15;
|
||||
let FILENAME = "";
|
||||
let SCALE = 20;
|
||||
let HOOP_DIAMETER = 0.625;
|
||||
|
||||
let img;
|
||||
|
||||
// set up element variables
|
||||
let imgElement = document.getElementById("imageSrc")
|
||||
let inputElement = document.getElementById("fileInput");
|
||||
inputElement.addEventListener("change", (e) => {
|
||||
imgElement.src = URL.createObjectURL(e.target.files[0]);
|
||||
}, false);
|
||||
let canvas = document.getElementById("canvasOutput");
|
||||
var ctx=canvas.getContext("2d");
|
||||
let canvas2 = document.getElementById("canvasOutput2");
|
||||
var ctx2=canvas.getContext("2d");
|
||||
let status = document.getElementById("status");
|
||||
|
||||
var bar1 = new ldBar("#loadingbar");
|
||||
var bar2 = document.getElementById('loadingbar').ldBar;
|
||||
bar1.set(0);
|
||||
|
||||
let length;
|
||||
var R = {};
|
||||
|
||||
//pre initilization
|
||||
let pin_coords;
|
||||
let center;
|
||||
let radius;
|
||||
|
||||
let line_cache_y;
|
||||
let line_cache_x;
|
||||
let line_cache_length;
|
||||
let line_cache_weight;
|
||||
|
||||
//line variables
|
||||
let error;
|
||||
let img_result;
|
||||
let result;
|
||||
let line_mask;
|
||||
|
||||
let line_sequence;
|
||||
let pin;
|
||||
let thread_length;
|
||||
let last_pins;
|
||||
|
||||
|
||||
imgElement.onload = function() {
|
||||
//initially load image to canvas
|
||||
let mat = cv.imread(imgElement, cv.IMREAD_GRAYSCALE);
|
||||
cv.imshow('canvasOutput', mat);
|
||||
mat.delete();
|
||||
|
||||
//read from canvas, make grayscale
|
||||
let src = cv.imread("canvasOutput", cv.IMREAD_GRAYSCALE);
|
||||
let img = new cv.Mat();
|
||||
cv.cvtColor(src, img, cv.COLOR_RGBA2GRAY, 0);
|
||||
cv.imshow('canvasOutput', img);
|
||||
length = img.rows;
|
||||
src.delete(); img.delete();
|
||||
|
||||
//circle crop canvas
|
||||
ctx.globalCompositeOperation='destination-in';
|
||||
ctx.beginPath();
|
||||
ctx.arc(length/2,length/2, length/2, 0, Math.PI*2);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
// fetch clean grayscale cropped image back from canvas
|
||||
src = cv.imread("canvasOutput");
|
||||
let rgbaPlanes = new cv.MatVector();
|
||||
cv.split(src, rgbaPlanes);
|
||||
// Get R channel because the other channels aren't necessary in grayscale
|
||||
R = rgbaPlanes.get(0);
|
||||
length = R.rows;
|
||||
src.delete(); rgbaPlanes.delete();
|
||||
|
||||
NonBlockingCalculatePins();
|
||||
|
||||
}
|
||||
|
||||
function NonBlockingCalculatePins(){
|
||||
// set up necessary variables
|
||||
console.log("Calculating pins...");
|
||||
status.textContent = "Calculating pins...";
|
||||
pin_coords = [];
|
||||
center = length / 2;
|
||||
radius = length / 2 - 1/2
|
||||
let i = 0;
|
||||
|
||||
(function codeBlock(){
|
||||
if(i < N_PINS){
|
||||
angle = 2 * Math.PI * i / N_PINS;
|
||||
pin_coords.push([Math.floor(center + radius * Math.cos(angle)),
|
||||
Math.floor(center + radius * Math.sin(angle))]);
|
||||
i++;
|
||||
setTimeout(codeBlock, 0);
|
||||
} else {
|
||||
console.log('Done Calculating pins');
|
||||
status.textContent = "Done Calculating pins";
|
||||
NonBlockingPrecalculateLines();
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function NonBlockingPrecalculateLines(){
|
||||
// set up necessary variables
|
||||
console.log("Precalculating all lines...");
|
||||
status.textContent = "Precalculating all lines...";
|
||||
line_cache_y = Array.apply(null, {length: (N_PINS * N_PINS)});
|
||||
line_cache_x = Array.apply(null, {length: (N_PINS * N_PINS)});
|
||||
line_cache_length = Array.apply(null, {length: (N_PINS * N_PINS)}).map(Function.call, function(){return 0;});
|
||||
line_cache_weight = Array.apply(null, {length: (N_PINS * N_PINS)}).map(Function.call, function(){return 1;});
|
||||
let a = 0;
|
||||
|
||||
(function codeBlock(){
|
||||
if(a < N_PINS){
|
||||
for (b = a + MIN_DISTANCE; b < N_PINS; b++) {
|
||||
x0 = pin_coords[a][0];
|
||||
y0 = pin_coords[a][1];
|
||||
|
||||
x1 = pin_coords[b][0];
|
||||
y1 = pin_coords[b][1];
|
||||
|
||||
d = Math.floor(Number(Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0)*(y1 - y0))));
|
||||
xs = linspace(x0, x1, d);
|
||||
ys = linspace(y0, y1, d);
|
||||
|
||||
line_cache_y[b*N_PINS + a] = ys;
|
||||
line_cache_y[a*N_PINS + b] = ys;
|
||||
line_cache_x[b*N_PINS + a] = xs;
|
||||
line_cache_x[a*N_PINS + b] = xs;
|
||||
line_cache_length[b*N_PINS + a] = d;
|
||||
line_cache_length[a*N_PINS + b] = d;
|
||||
}
|
||||
a++;
|
||||
setTimeout(codeBlock, 0);
|
||||
} else {
|
||||
console.log('Done Precalculating Lines');
|
||||
status.textContent = "Done Precalculating Lines";
|
||||
NonBlockingLineCalculator();
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function NonBlockingLineCalculator(){
|
||||
// set up necessary variables
|
||||
console.log("Drawing Lines...");
|
||||
status.textContent = "Drawing Lines...";
|
||||
error = nj.ones([R.rows, R.cols]).multiply(0xff).subtract(nj.uint8(R.data).reshape(R.rows,R.cols));
|
||||
img_result = nj.ones([R.cols, R.rows ]).multiply(0xff);
|
||||
result = nj.ones([R.cols * SCALE, R.rows * SCALE]).multiply(0xff);
|
||||
result = new cv.matFromArray(R.cols * SCALE, R.rows * SCALE, cv.CV_8UC1, result.selection.data);
|
||||
line_mask = nj.zeros([R.cols, R.rows], 'float64');
|
||||
|
||||
line_sequence = [];
|
||||
pin = 0;
|
||||
line_sequence.push(pin);
|
||||
thread_length = 0;
|
||||
last_pins = [];
|
||||
let l = 0;
|
||||
|
||||
(function codeBlock(){
|
||||
if(l < MAX_LINES){
|
||||
if(l%10 == 0){
|
||||
draw();
|
||||
}
|
||||
|
||||
max_err = -1;
|
||||
best_pin = -1;
|
||||
|
||||
for(offset=MIN_DISTANCE; offset < N_PINS - MIN_DISTANCE; offset++){
|
||||
test_pin = (pin + offset) % N_PINS;
|
||||
if(last_pins.includes(test_pin)){
|
||||
continue;
|
||||
}else {
|
||||
|
||||
xs = line_cache_x[test_pin * N_PINS + pin];
|
||||
ys = line_cache_y[test_pin * N_PINS + pin];
|
||||
|
||||
line_err = getLineErr(error, ys, xs) * line_cache_weight[test_pin * N_PINS + pin];
|
||||
|
||||
if( line_err > max_err){
|
||||
max_err = line_err;
|
||||
best_pin = test_pin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line_sequence.push(best_pin);
|
||||
|
||||
xs = line_cache_x[best_pin * N_PINS + pin];
|
||||
ys = line_cache_y[best_pin * N_PINS + pin];
|
||||
weight = LINE_WEIGHT * line_cache_weight[best_pin * N_PINS + pin];
|
||||
|
||||
line_mask = nj.zeros([R.cols, R.rows], 'float64');
|
||||
line_mask = setLine(line_mask, ys, xs, weight);
|
||||
error = subtractArrays(error, line_mask);
|
||||
|
||||
|
||||
|
||||
p = new cv.Point(pin_coords[pin][0] * SCALE, pin_coords[pin][1] * SCALE);
|
||||
p2 = new cv.Point(pin_coords[best_pin][0] * SCALE, pin_coords[best_pin][1] * SCALE);
|
||||
cv.line(result, p, p2, new cv.Scalar(0, 0, 0), 4, 8, 0);
|
||||
|
||||
x0 = pin_coords[pin][0];
|
||||
y0 = pin_coords[pin][1];
|
||||
|
||||
x1 = pin_coords[best_pin][0];
|
||||
y1 = pin_coords[best_pin][1];
|
||||
|
||||
dist = Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
|
||||
thread_length += HOOP_DIAMETER / length * dist;
|
||||
|
||||
last_pins.push(best_pin);
|
||||
if(last_pins.length > 20){
|
||||
last_pins.shift();
|
||||
}
|
||||
pin = best_pin;
|
||||
|
||||
//update loading bar
|
||||
bar1.set(Math.round((l / MAX_LINES) * 100))
|
||||
status.textContent = l + " Lines drawn";
|
||||
|
||||
l++;
|
||||
setTimeout(codeBlock, 0);
|
||||
} else {
|
||||
console.log('Done Drawing Lines');
|
||||
Finalize();
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function draw(){
|
||||
let dsize = new cv.Size(R.cols, R.rows);
|
||||
let dst = new cv.Mat();
|
||||
cv.resize(result, dst, dsize, 0, 0, cv.INTER_AREA);
|
||||
cv.imshow('canvasOutput2', dst);
|
||||
dst.delete();
|
||||
}
|
||||
|
||||
function Finalize() {
|
||||
let dsize = new cv.Size(R.cols, R.rows);
|
||||
let dst = new cv.Mat();
|
||||
cv.resize(result, dst, dsize, 0, 0, cv.INTER_AREA);
|
||||
img_result = nj.uint8(dst.data).reshape(R.rows,R.cols);
|
||||
|
||||
|
||||
let diff = img_result.subtract(nj.uint8(R.data).reshape(R.rows,R.cols));
|
||||
mul = nj.uint8(compareMul(img_result.selection.data, R.data)).reshape(R.rows,R.cols);
|
||||
absdiff = compareAbsdiff(diff.selection.data, mul.selection.data);
|
||||
|
||||
console.log(getSum(absdiff) / (length * length));
|
||||
console.log("complete");
|
||||
|
||||
cv.imshow('canvasOutput2', dst);
|
||||
console.log(line_sequence);
|
||||
status.textContent = "Complete";
|
||||
dst.delete();
|
||||
}
|
||||
|
||||
function getLineErr(arr, coords1, coords2){
|
||||
let result = new Uint8Array(coords1.length);
|
||||
for(i=0;i<coords1.length;i++){
|
||||
result[i] = arr.get(coords1[i], coords2[i]);
|
||||
}
|
||||
return getSum(result);
|
||||
}
|
||||
|
||||
function setLine(arr, coords1, coords2, line){
|
||||
for(i=0;i<coords1.length;i++){
|
||||
arr.set(coords1[i], coords2[i], line);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function compareMul(arr1, arr2){
|
||||
let result = new Uint8Array(arr1.length);
|
||||
for(i=0;i<arr1.length;i++){
|
||||
result[i] = (arr1[i] < arr2[i]) * 254 + 1 ;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function compareAbsdiff(arr1, arr2){
|
||||
let rsult = new Uint8Array(arr1.length);
|
||||
for(i=0;i<arr1.length;i++){
|
||||
rsult[i] = (arr1[i] * arr2[i]);
|
||||
}
|
||||
return rsult;
|
||||
}
|
||||
|
||||
function subtractArrays(arr1, arr2) {
|
||||
for(i=0; i<arr1.selection.data.length;i++){
|
||||
arr1.selection.data[i] = arr1.selection.data[i] - arr2.selection.data[i]
|
||||
if(arr1.selection.data[i] < 0){
|
||||
arr1.selection.data[i] = 0;
|
||||
}else if (arr1.selection.data[i] > 255){
|
||||
arr1.selection.data[i] = 255;
|
||||
}
|
||||
}
|
||||
return arr1;
|
||||
}
|
||||
|
||||
function subtractArraysSimple(arr1, arr2) {
|
||||
for(i=0; i<arr1.length;i++){
|
||||
arr1[i] = arr1[i] - arr2[i];
|
||||
}
|
||||
return arr1;
|
||||
}
|
||||
|
||||
function getSum(arr) {
|
||||
let v = 0;
|
||||
for(i=0;i<arr.length;i++){
|
||||
v = v + arr[i];
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
function makeArr(startValue, stopValue, cardinality) {
|
||||
var arr = [];
|
||||
var currValue = startValue;
|
||||
var step = (stopValue - startValue) / (cardinality - 1);
|
||||
for (var i = 0; i < cardinality; i++) {
|
||||
arr.push(Math.round(currValue + (step * i)));
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function AddRGB(arr1, arr2, arr3){
|
||||
for(i=0;i<arr1.data.length;i++){
|
||||
var avg = (arr1.data[i] + arr2.data[i] + arr3.data[i]);
|
||||
arr1.data[i] = avg;
|
||||
}
|
||||
return arr1;
|
||||
}
|
||||
|
||||
function linspace(a,b,n) {
|
||||
if(typeof n === "undefined") n = Math.max(Math.round(b-a)+1,1);
|
||||
if(n<2) { return n===1?[a]:[]; }
|
||||
var i,ret = Array(n);
|
||||
n--;
|
||||
for(i=n;i>=0;i--) { ret[i] = Math.floor((i*b+(n-i)*a)/n); }
|
||||
return ret;
|
||||
}
|
||||
|
||||
function onOpenCvReady() {
|
||||
// even when this is called, sometimes it's still not ready, adding slight time buffer
|
||||
setTimeout(function(){
|
||||
document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
|
||||
}, 1000);
|
||||
}
|
||||
</script>
|
||||
<script async src="opencv.js" onload="onOpenCvReady();" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
.ldBar{position:relative;}.ldBar.label-center > .ldBar-label{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);text-shadow:0 0 3px #fff}.ldBar-label:after{content:"%";display:inline}.ldBar.no-percent .ldBar-label:after{content:""}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,353 @@
|
|||
function iota(n) {
|
||||
var result = new Array(n)
|
||||
for(var i=0; i<n; ++i) {
|
||||
result[i] = i
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function isBuffer (obj) {
|
||||
return obj != null && obj.constructor != null &&
|
||||
typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)
|
||||
}
|
||||
|
||||
var hasTypedArrays = ((typeof Float64Array) !== "undefined")
|
||||
|
||||
function compare1st(a, b) {
|
||||
return a[0] - b[0]
|
||||
}
|
||||
|
||||
function order() {
|
||||
var stride = this.stride
|
||||
var terms = new Array(stride.length)
|
||||
var i
|
||||
for(i=0; i<terms.length; ++i) {
|
||||
terms[i] = [Math.abs(stride[i]), i]
|
||||
}
|
||||
terms.sort(compare1st)
|
||||
var result = new Array(terms.length)
|
||||
for(i=0; i<result.length; ++i) {
|
||||
result[i] = terms[i][1]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function compileConstructor(dtype, dimension) {
|
||||
var className = ["View", dimension, "d", dtype].join("")
|
||||
if(dimension < 0) {
|
||||
className = "View_Nil" + dtype
|
||||
}
|
||||
var useGetters = (dtype === "generic")
|
||||
|
||||
if(dimension === -1) {
|
||||
//Special case for trivial arrays
|
||||
var code =
|
||||
"function "+className+"(a){this.data=a;};\
|
||||
var proto="+className+".prototype;\
|
||||
proto.dtype='"+dtype+"';\
|
||||
proto.index=function(){return -1};\
|
||||
proto.size=0;\
|
||||
proto.dimension=-1;\
|
||||
proto.shape=proto.stride=proto.order=[];\
|
||||
proto.lo=proto.hi=proto.transpose=proto.step=\
|
||||
function(){return new "+className+"(this.data);};\
|
||||
proto.get=proto.set=function(){};\
|
||||
proto.pick=function(){return null};\
|
||||
return function construct_"+className+"(a){return new "+className+"(a);}"
|
||||
var procedure = new Function(code)
|
||||
return procedure()
|
||||
} else if(dimension === 0) {
|
||||
//Special case for 0d arrays
|
||||
var code =
|
||||
"function "+className+"(a,d) {\
|
||||
this.data = a;\
|
||||
this.offset = d\
|
||||
};\
|
||||
var proto="+className+".prototype;\
|
||||
proto.dtype='"+dtype+"';\
|
||||
proto.index=function(){return this.offset};\
|
||||
proto.dimension=0;\
|
||||
proto.size=1;\
|
||||
proto.shape=\
|
||||
proto.stride=\
|
||||
proto.order=[];\
|
||||
proto.lo=\
|
||||
proto.hi=\
|
||||
proto.transpose=\
|
||||
proto.step=function "+className+"_copy() {\
|
||||
return new "+className+"(this.data,this.offset)\
|
||||
};\
|
||||
proto.pick=function "+className+"_pick(){\
|
||||
return TrivialArray(this.data);\
|
||||
};\
|
||||
proto.valueOf=proto.get=function "+className+"_get(){\
|
||||
return "+(useGetters ? "this.data.get(this.offset)" : "this.data[this.offset]")+
|
||||
"};\
|
||||
proto.set=function "+className+"_set(v){\
|
||||
return "+(useGetters ? "this.data.set(this.offset,v)" : "this.data[this.offset]=v")+"\
|
||||
};\
|
||||
return function construct_"+className+"(a,b,c,d){return new "+className+"(a,d)}"
|
||||
var procedure = new Function("TrivialArray", code)
|
||||
return procedure(CACHED_CONSTRUCTORS[dtype][0])
|
||||
}
|
||||
|
||||
var code = ["'use strict'"]
|
||||
|
||||
//Create constructor for view
|
||||
var indices = iota(dimension)
|
||||
var args = indices.map(function(i) { return "i"+i })
|
||||
var index_str = "this.offset+" + indices.map(function(i) {
|
||||
return "this.stride[" + i + "]*i" + i
|
||||
}).join("+")
|
||||
var shapeArg = indices.map(function(i) {
|
||||
return "b"+i
|
||||
}).join(",")
|
||||
var strideArg = indices.map(function(i) {
|
||||
return "c"+i
|
||||
}).join(",")
|
||||
code.push(
|
||||
"function "+className+"(a," + shapeArg + "," + strideArg + ",d){this.data=a",
|
||||
"this.shape=[" + shapeArg + "]",
|
||||
"this.stride=[" + strideArg + "]",
|
||||
"this.offset=d|0}",
|
||||
"var proto="+className+".prototype",
|
||||
"proto.dtype='"+dtype+"'",
|
||||
"proto.dimension="+dimension)
|
||||
|
||||
//view.size:
|
||||
code.push("Object.defineProperty(proto,'size',{get:function "+className+"_size(){\
|
||||
return "+indices.map(function(i) { return "this.shape["+i+"]" }).join("*"),
|
||||
"}})")
|
||||
|
||||
//view.order:
|
||||
if(dimension === 1) {
|
||||
code.push("proto.order=[0]")
|
||||
} else {
|
||||
code.push("Object.defineProperty(proto,'order',{get:")
|
||||
if(dimension < 4) {
|
||||
code.push("function "+className+"_order(){")
|
||||
if(dimension === 2) {
|
||||
code.push("return (Math.abs(this.stride[0])>Math.abs(this.stride[1]))?[1,0]:[0,1]}})")
|
||||
} else if(dimension === 3) {
|
||||
code.push(
|
||||
"var s0=Math.abs(this.stride[0]),s1=Math.abs(this.stride[1]),s2=Math.abs(this.stride[2]);\
|
||||
if(s0>s1){\
|
||||
if(s1>s2){\
|
||||
return [2,1,0];\
|
||||
}else if(s0>s2){\
|
||||
return [1,2,0];\
|
||||
}else{\
|
||||
return [1,0,2];\
|
||||
}\
|
||||
}else if(s0>s2){\
|
||||
return [2,0,1];\
|
||||
}else if(s2>s1){\
|
||||
return [0,1,2];\
|
||||
}else{\
|
||||
return [0,2,1];\
|
||||
}}})")
|
||||
}
|
||||
} else {
|
||||
code.push("ORDER})")
|
||||
}
|
||||
}
|
||||
|
||||
//view.set(i0, ..., v):
|
||||
code.push(
|
||||
"proto.set=function "+className+"_set("+args.join(",")+",v){")
|
||||
if(useGetters) {
|
||||
code.push("return this.data.set("+index_str+",v)}")
|
||||
} else {
|
||||
code.push("return this.data["+index_str+"]=v}")
|
||||
}
|
||||
|
||||
//view.get(i0, ...):
|
||||
code.push("proto.get=function "+className+"_get("+args.join(",")+"){")
|
||||
if(useGetters) {
|
||||
code.push("return this.data.get("+index_str+")}")
|
||||
} else {
|
||||
code.push("return this.data["+index_str+"]}")
|
||||
}
|
||||
|
||||
//view.index:
|
||||
code.push(
|
||||
"proto.index=function "+className+"_index(", args.join(), "){return "+index_str+"}")
|
||||
|
||||
//view.hi():
|
||||
code.push("proto.hi=function "+className+"_hi("+args.join(",")+"){return new "+className+"(this.data,"+
|
||||
indices.map(function(i) {
|
||||
return ["(typeof i",i,"!=='number'||i",i,"<0)?this.shape[", i, "]:i", i,"|0"].join("")
|
||||
}).join(",")+","+
|
||||
indices.map(function(i) {
|
||||
return "this.stride["+i + "]"
|
||||
}).join(",")+",this.offset)}")
|
||||
|
||||
//view.lo():
|
||||
var a_vars = indices.map(function(i) { return "a"+i+"=this.shape["+i+"]" })
|
||||
var c_vars = indices.map(function(i) { return "c"+i+"=this.stride["+i+"]" })
|
||||
code.push("proto.lo=function "+className+"_lo("+args.join(",")+"){var b=this.offset,d=0,"+a_vars.join(",")+","+c_vars.join(","))
|
||||
for(var i=0; i<dimension; ++i) {
|
||||
code.push(
|
||||
"if(typeof i"+i+"==='number'&&i"+i+">=0){\
|
||||
d=i"+i+"|0;\
|
||||
b+=c"+i+"*d;\
|
||||
a"+i+"-=d}")
|
||||
}
|
||||
code.push("return new "+className+"(this.data,"+
|
||||
indices.map(function(i) {
|
||||
return "a"+i
|
||||
}).join(",")+","+
|
||||
indices.map(function(i) {
|
||||
return "c"+i
|
||||
}).join(",")+",b)}")
|
||||
|
||||
//view.step():
|
||||
code.push("proto.step=function "+className+"_step("+args.join(",")+"){var "+
|
||||
indices.map(function(i) {
|
||||
return "a"+i+"=this.shape["+i+"]"
|
||||
}).join(",")+","+
|
||||
indices.map(function(i) {
|
||||
return "b"+i+"=this.stride["+i+"]"
|
||||
}).join(",")+",c=this.offset,d=0,ceil=Math.ceil")
|
||||
for(var i=0; i<dimension; ++i) {
|
||||
code.push(
|
||||
"if(typeof i"+i+"==='number'){\
|
||||
d=i"+i+"|0;\
|
||||
if(d<0){\
|
||||
c+=b"+i+"*(a"+i+"-1);\
|
||||
a"+i+"=ceil(-a"+i+"/d)\
|
||||
}else{\
|
||||
a"+i+"=ceil(a"+i+"/d)\
|
||||
}\
|
||||
b"+i+"*=d\
|
||||
}")
|
||||
}
|
||||
code.push("return new "+className+"(this.data,"+
|
||||
indices.map(function(i) {
|
||||
return "a" + i
|
||||
}).join(",")+","+
|
||||
indices.map(function(i) {
|
||||
return "b" + i
|
||||
}).join(",")+",c)}")
|
||||
|
||||
//view.transpose():
|
||||
var tShape = new Array(dimension)
|
||||
var tStride = new Array(dimension)
|
||||
for(var i=0; i<dimension; ++i) {
|
||||
tShape[i] = "a[i"+i+"]"
|
||||
tStride[i] = "b[i"+i+"]"
|
||||
}
|
||||
code.push("proto.transpose=function "+className+"_transpose("+args+"){"+
|
||||
args.map(function(n,idx) { return n + "=(" + n + "===undefined?" + idx + ":" + n + "|0)"}).join(";"),
|
||||
"var a=this.shape,b=this.stride;return new "+className+"(this.data,"+tShape.join(",")+","+tStride.join(",")+",this.offset)}")
|
||||
|
||||
//view.pick():
|
||||
code.push("proto.pick=function "+className+"_pick("+args+"){var a=[],b=[],c=this.offset")
|
||||
for(var i=0; i<dimension; ++i) {
|
||||
code.push("if(typeof i"+i+"==='number'&&i"+i+">=0){c=(c+this.stride["+i+"]*i"+i+")|0}else{a.push(this.shape["+i+"]);b.push(this.stride["+i+"])}")
|
||||
}
|
||||
code.push("var ctor=CTOR_LIST[a.length+1];return ctor(this.data,a,b,c)}")
|
||||
|
||||
//Add return statement
|
||||
code.push("return function construct_"+className+"(data,shape,stride,offset){return new "+className+"(data,"+
|
||||
indices.map(function(i) {
|
||||
return "shape["+i+"]"
|
||||
}).join(",")+","+
|
||||
indices.map(function(i) {
|
||||
return "stride["+i+"]"
|
||||
}).join(",")+",offset)}")
|
||||
|
||||
//Compile procedure
|
||||
var procedure = new Function("CTOR_LIST", "ORDER", code.join("\n"))
|
||||
return procedure(CACHED_CONSTRUCTORS[dtype], order)
|
||||
}
|
||||
|
||||
function arrayDType(data) {
|
||||
if(isBuffer(data)) {
|
||||
return "buffer"
|
||||
}
|
||||
if(hasTypedArrays) {
|
||||
switch(Object.prototype.toString.call(data)) {
|
||||
case "[object Float64Array]":
|
||||
return "float64"
|
||||
case "[object Float32Array]":
|
||||
return "float32"
|
||||
case "[object Int8Array]":
|
||||
return "int8"
|
||||
case "[object Int16Array]":
|
||||
return "int16"
|
||||
case "[object Int32Array]":
|
||||
return "int32"
|
||||
case "[object Uint8Array]":
|
||||
return "uint8"
|
||||
case "[object Uint16Array]":
|
||||
return "uint16"
|
||||
case "[object Uint32Array]":
|
||||
return "uint32"
|
||||
case "[object Uint8ClampedArray]":
|
||||
return "uint8_clamped"
|
||||
}
|
||||
}
|
||||
if(Array.isArray(data)) {
|
||||
return "array"
|
||||
}
|
||||
return "generic"
|
||||
}
|
||||
|
||||
var CACHED_CONSTRUCTORS = {
|
||||
"float32":[],
|
||||
"float64":[],
|
||||
"int8":[],
|
||||
"int16":[],
|
||||
"int32":[],
|
||||
"uint8":[],
|
||||
"uint16":[],
|
||||
"uint32":[],
|
||||
"array":[],
|
||||
"uint8_clamped":[],
|
||||
"buffer":[],
|
||||
"generic":[]
|
||||
}
|
||||
|
||||
;(function() {
|
||||
for(var id in CACHED_CONSTRUCTORS) {
|
||||
CACHED_CONSTRUCTORS[id].push(compileConstructor(id, -1))
|
||||
}
|
||||
});
|
||||
|
||||
function wrappedNDArrayCtor(data, shape, stride, offset) {
|
||||
if(data === undefined) {
|
||||
var ctor = CACHED_CONSTRUCTORS.array[0]
|
||||
return ctor([])
|
||||
} else if(typeof data === "number") {
|
||||
data = [data]
|
||||
}
|
||||
if(shape === undefined) {
|
||||
shape = [ data.length ]
|
||||
}
|
||||
var d = shape.length
|
||||
if(stride === undefined) {
|
||||
stride = new Array(d)
|
||||
for(var i=d-1, sz=1; i>=0; --i) {
|
||||
stride[i] = sz
|
||||
sz *= shape[i]
|
||||
}
|
||||
}
|
||||
if(offset === undefined) {
|
||||
offset = 0
|
||||
for(var i=0; i<d; ++i) {
|
||||
if(stride[i] < 0) {
|
||||
offset -= (shape[i]-1)*stride[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
var dtype = arrayDType(data)
|
||||
var ctor_list = CACHED_CONSTRUCTORS[dtype]
|
||||
while(ctor_list.length <= d+1) {
|
||||
ctor_list.push(compileConstructor(dtype, ctor_list.length-1))
|
||||
}
|
||||
var ctor = ctor_list[d+1]
|
||||
return ctor(data, shape, stride, offset)
|
||||
}
|
||||
|
||||
//module.exports = wrappedNDArrayCtor
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,193 @@
|
|||
var collections = require('collections');
|
||||
var math = require('math');
|
||||
var os = require('os');
|
||||
var cv2 = require('cv2');
|
||||
var np = require('numpy');
|
||||
var time = require('time');
|
||||
|
||||
MAX_LINES = 4000;
|
||||
N_PINS = 36*8;
|
||||
MIN_LOOP = 20 // To avoid getting stuck in a loop
|
||||
MIN_DISTANCE = 20 // To avoid very short lines
|
||||
LINE_WEIGHT = 15 // Tweakable parameter
|
||||
FILENAME = 'leki-lig.jpg';
|
||||
SCALE = 25 // For making a very high resolution render, to attempt to accurately gauge how thick the thread must be
|
||||
HOOP_DIAMETER = 0.625 // To calculate total thread length
|
||||
|
||||
tic = time.perf_counter();
|
||||
|
||||
img = cv2.imread(FILENAME, cv2.IMREAD_GRAYSCALE);
|
||||
|
||||
// Didn't bother to make it work for non-square images
|
||||
assert img.shape[0] == img.shape[1];
|
||||
length = img.shape[0];
|
||||
|
||||
function disp(image) {
|
||||
cv2.imshow('image', image);
|
||||
cv2.waitKey(0);
|
||||
cv2.destroyAllWindows();
|
||||
}
|
||||
// Cut away everything around a central circle
|
||||
X,Y = np.ogrid[0:length, 0:length];
|
||||
circlemask = (X - length/2) ** 2 + (Y - length/2) ** 2 > length/2 * length/2;
|
||||
img[circlemask] = 0xFF;
|
||||
|
||||
pin_coords = [];
|
||||
center = length / 2;
|
||||
radius = length / 2 - 1/2;
|
||||
|
||||
// Precalculate the coordinates of every pin
|
||||
for (i in range(N_PINS)) {
|
||||
angle = 2 * math.pi * i / N_PINS;
|
||||
pin_coords.push((math.floor(center + radius * math.cos(angle)),
|
||||
math.floor(center + radius * math.sin(angle))));
|
||||
}
|
||||
line_cache_y = [null] * N_PINS * N_PINS;
|
||||
|
||||
line_cache_x = [null] * N_PINS * N_PINS;
|
||||
line_cache_weight = [1] * N_PINS * N_PINS // Turned out to be unnecessary, unused
|
||||
line_cache_length = [0] * N_PINS * N_PINS;
|
||||
|
||||
console.log('Precalculating all lines... ', end='', flush=true);
|
||||
|
||||
for (a in range(N_PINS)) {
|
||||
for (b in range(a + MIN_DISTANCE, N_PINS)) {
|
||||
x0 = pin_coords[a][0];
|
||||
y0 = pin_coords[a][1];
|
||||
|
||||
x1 = pin_coords[b][0];
|
||||
y1 = pin_coords[b][1];
|
||||
|
||||
d = Number(math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0)*(y1 - y0)));
|
||||
|
||||
// A proper (slower) Bresenham does not give any better result *shrug*
|
||||
xs = np.linspace(x0, x1, d, dtype=int);
|
||||
ys = np.linspace(y0, y1, d, dtype=int);
|
||||
|
||||
line_cache_y[b*N_PINS + a] = ys;
|
||||
line_cache_y[a*N_PINS + b] = ys;
|
||||
line_cache_x[b*N_PINS + a] = xs;
|
||||
line_cache_x[a*N_PINS + b] = xs;
|
||||
line_cache_length[b*N_PINS + a] = d;
|
||||
line_cache_length[a*N_PINS + b] = d;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
console.log('done');
|
||||
|
||||
error = np.ones(img.shape) * 0xFF - img.copy();
|
||||
|
||||
img_result = np.ones(img.shape) * 0xFF;
|
||||
lse_buffer = np.ones(img.shape) * 0xFF // Used in the unused LSE algorithm
|
||||
|
||||
result = np.ones((img.shape[0] * SCALE, img.shape[1] * SCALE), np.uint8) * 0xFF;
|
||||
line_mask = np.zeros(img.shape, np.float64) // XXX
|
||||
|
||||
line_sequence = [];
|
||||
pin = 0;
|
||||
line_sequence.push(pin);
|
||||
|
||||
thread_length = 0;
|
||||
|
||||
last_pins = collections.deque(maxlen = MIN_LOOP);
|
||||
|
||||
for (l in range(MAX_LINES)) {
|
||||
|
||||
if (l % 100 == 0) {
|
||||
console.log('%d ' % l, end='', flush=true);
|
||||
}
|
||||
img_result = cv2.resize(result, img.shape, Numbererpolation=cv2.INTER_AREA);
|
||||
}
|
||||
// Some trickery to fast calculate the absolute difference, to estimate the error per pixel
|
||||
diff = img_result - img;
|
||||
mul = np.uint8(img_result < img) * 254 + 1;
|
||||
absdiff = diff * mul;
|
||||
console.log(absdiff.sum() / (length * length));
|
||||
|
||||
max_err = -math.inf;
|
||||
best_pin = -1;
|
||||
|
||||
// Find the line which will lower the error the most
|
||||
for (offset in range(MIN_DISTANCE, N_PINS - MIN_DISTANCE)) {
|
||||
test_pin = (pin + offset) % N_PINS;
|
||||
if (test_pin in last_pins) {
|
||||
continue;
|
||||
}
|
||||
xs = line_cache_x[test_pin * N_PINS + pin];
|
||||
ys = line_cache_y[test_pin * N_PINS + pin];
|
||||
}
|
||||
// Simple
|
||||
// Error defined as the sum of the brightness of each pixel in the original
|
||||
// The idea being that a wire can only darken pixels in the result
|
||||
line_err = np.sum(error[ys,xs]) * line_cache_weight[test_pin*N_PINS + pin];
|
||||
'\n' +
|
||||
' # LSE Unused\n' +
|
||||
' goal_pixels = img[ys, xs]\n' +
|
||||
' old_pixels = lse_buffer[ys, xs]\n' +
|
||||
' new_pixels = np.clip(old_pixels - LINE_WEIGHT * line_cache_weight[test_pin*N_PINS + pin], 0, 255)\n' +
|
||||
' line_err = np.sum((old_pixels - goal_pixels) ** 2) - np.sum((new_pixels - goal_pixels) ** 2)\n' +
|
||||
' #LSE\n' +
|
||||
' ';
|
||||
|
||||
if (line_err > max_err) {
|
||||
max_err = line_err;
|
||||
best_pin = test_pin;
|
||||
}
|
||||
line_sequence.push(best_pin);
|
||||
|
||||
xs = line_cache_x[best_pin * N_PINS + pin];
|
||||
ys = line_cache_y[best_pin * N_PINS + pin];
|
||||
weight = LINE_WEIGHT * line_cache_weight[best_pin*N_PINS + pin];
|
||||
|
||||
'\n' +
|
||||
' #LSE\n' +
|
||||
' old_pixels = lse_buffer[ys, xs]\n' +
|
||||
' new_pixels = np.clip(old_pixels - weight, 0, 255)\n' +
|
||||
' lse_buffer[ys, xs] = new_pixels\n' +
|
||||
' #LSE\n' +
|
||||
' ';
|
||||
|
||||
// Subtract the line from the error
|
||||
line_mask.fill(0);
|
||||
line_mask[ys, xs] = weight;
|
||||
error = error - line_mask;
|
||||
error.clip(0, 255);
|
||||
|
||||
// Draw the line in the result
|
||||
cv2.line(result,
|
||||
(pin_coords[pin][0] * SCALE, pin_coords[pin][1] * SCALE),
|
||||
(pin_coords[best_pin][0] * SCALE, pin_coords[best_pin][1] * SCALE),
|
||||
color=0, thickness=4, lineType=8);
|
||||
|
||||
x0 = pin_coords[pin][0];
|
||||
y0 = pin_coords[pin][1];
|
||||
|
||||
x1 = pin_coords[best_pin][0];
|
||||
y1 = pin_coords[best_pin][1];
|
||||
|
||||
// Calculate physical distance
|
||||
dist = math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0)*(y1 - y0));
|
||||
thread_length += HOOP_DIAMETER / length * dist;
|
||||
|
||||
last_pins.push(best_pin);
|
||||
pin = best_pin;
|
||||
|
||||
img_result = cv2.resize(result, img.shape, Numbererpolation=cv2.INTER_AREA);
|
||||
|
||||
diff = img_result - img;
|
||||
mul = np.uint8(img_result < img) * 254 + 1;
|
||||
absdiff = diff * mul;
|
||||
|
||||
console.log(absdiff.sum() / (length * length));
|
||||
|
||||
console.log('\x07');
|
||||
toc = time.perf_counter();
|
||||
console.log('%.1f seconds' % (toc - tic));
|
||||
|
||||
cv2.imwrite(os.path.splitext(FILENAME)[0] + '-out.png', result);
|
||||
|
||||
with open(os.path.splitext(FILENAME)[0] + '.json', 'w') as f) {
|
||||
f.write(str(line_sequence)) ;
|
||||
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
import collections
|
||||
import math
|
||||
import os
|
||||
import cv2
|
||||
import numpy as np
|
||||
import time
|
||||
|
||||
MAX_LINES = 4000
|
||||
N_PINS = 36*8
|
||||
MIN_LOOP = 20 # To avoid getting stuck in a loop
|
||||
MIN_DISTANCE = 20 # To avoid very short lines
|
||||
LINE_WEIGHT = 15 # Tweakable parameter
|
||||
FILENAME = "h2.jpg"
|
||||
SCALE = 25 # For making a very high resolution render, to attempt to accurately gauge how thick the thread must be
|
||||
HOOP_DIAMETER = 0.625 # To calculate total thread length
|
||||
|
||||
tic = time.perf_counter()
|
||||
|
||||
img = cv2.imread(FILENAME, cv2.IMREAD_GRAYSCALE)
|
||||
|
||||
# Didn't bother to make it work for non-square images
|
||||
assert img.shape[0] == img.shape[1]
|
||||
length = img.shape[0]
|
||||
|
||||
def disp(image):
|
||||
cv2.imshow('image', image)
|
||||
cv2.waitKey(0)
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
# Cut away everything around a central circle
|
||||
X,Y = np.ogrid[0:length, 0:length]
|
||||
circlemask = (X - length/2) ** 2 + (Y - length/2) ** 2 > length/2 * length/2
|
||||
img[circlemask] = 0xFF
|
||||
breakpoint()
|
||||
pin_coords = []
|
||||
center = length / 2
|
||||
radius = length / 2 - 1/2
|
||||
|
||||
# Precalculate the coordinates of every pin
|
||||
for i in range(N_PINS):
|
||||
angle = 2 * math.pi * i / N_PINS
|
||||
pin_coords.append((math.floor(center + radius * math.cos(angle)),
|
||||
math.floor(center + radius * math.sin(angle))))
|
||||
|
||||
line_cache_y = [None] * N_PINS * N_PINS
|
||||
line_cache_x = [None] * N_PINS * N_PINS
|
||||
line_cache_weight = [1] * N_PINS * N_PINS # Turned out to be unnecessary, unused
|
||||
line_cache_length = [0] * N_PINS * N_PINS
|
||||
|
||||
print("Precalculating all lines... ", end='', flush=True)
|
||||
|
||||
for a in range(N_PINS):
|
||||
for b in range(a + MIN_DISTANCE, N_PINS):
|
||||
x0 = pin_coords[a][0]
|
||||
y0 = pin_coords[a][1]
|
||||
|
||||
x1 = pin_coords[b][0]
|
||||
y1 = pin_coords[b][1]
|
||||
|
||||
d = int(math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0)*(y1 - y0)))
|
||||
|
||||
#d = max(abs(y1-y0), abs(x1-x0)) inf-norm
|
||||
|
||||
# A proper (slower) Bresenham does not give any better result *shrug*
|
||||
xs = np.linspace(x0, x1, d, dtype=int)
|
||||
ys = np.linspace(y0, y1, d, dtype=int)
|
||||
|
||||
line_cache_y[b*N_PINS + a] = ys
|
||||
line_cache_y[a*N_PINS + b] = ys
|
||||
line_cache_x[b*N_PINS + a] = xs
|
||||
line_cache_x[a*N_PINS + b] = xs
|
||||
line_cache_length[b*N_PINS + a] = d
|
||||
line_cache_length[a*N_PINS + b] = d
|
||||
|
||||
|
||||
print("done")
|
||||
|
||||
error = np.ones(img.shape) * 0xFF - img.copy()
|
||||
|
||||
img_result = np.ones(img.shape) * 0xFF
|
||||
lse_buffer = np.ones(img.shape) * 0xFF # Used in the unused LSE algorithm
|
||||
|
||||
result = np.ones((img.shape[0] * SCALE, img.shape[1] * SCALE), np.uint8) * 0xFF
|
||||
line_mask = np.zeros(img.shape, np.float64) # XXX
|
||||
|
||||
line_sequence = []
|
||||
pin = 0
|
||||
line_sequence.append(pin)
|
||||
|
||||
thread_length = 0
|
||||
|
||||
last_pins = collections.deque(maxlen = MIN_LOOP)
|
||||
|
||||
for l in range(MAX_LINES):
|
||||
|
||||
if l % 100 == 0:
|
||||
print("%d " % l, end='', flush=True)
|
||||
|
||||
img_result = cv2.resize(result, img.shape, interpolation=cv2.INTER_AREA)
|
||||
|
||||
# Some trickery to fast calculate the absolute difference, to estimate the error per pixel
|
||||
diff = img_result - img
|
||||
mul = np.uint8(img_result < img) * 254 + 1
|
||||
absdiff = diff * mul
|
||||
print(absdiff.sum() / (length * length))
|
||||
|
||||
max_err = -math.inf
|
||||
best_pin = -1
|
||||
|
||||
# Find the line which will lower the error the most
|
||||
for offset in range(MIN_DISTANCE, N_PINS - MIN_DISTANCE):
|
||||
test_pin = (pin + offset) % N_PINS
|
||||
if test_pin in last_pins:
|
||||
continue
|
||||
|
||||
xs = line_cache_x[test_pin * N_PINS + pin]
|
||||
ys = line_cache_y[test_pin * N_PINS + pin]
|
||||
|
||||
# Simple
|
||||
# Error defined as the sum of the brightness of each pixel in the original
|
||||
# The idea being that a wire can only darken pixels in the result
|
||||
line_err = np.sum(error[ys,xs]) * line_cache_weight[test_pin*N_PINS + pin]
|
||||
'''
|
||||
|
||||
# LSE Unused
|
||||
goal_pixels = img[ys, xs]
|
||||
old_pixels = lse_buffer[ys, xs]
|
||||
new_pixels = np.clip(old_pixels - LINE_WEIGHT * line_cache_weight[test_pin*N_PINS + pin], 0, 255)
|
||||
|
||||
line_err = np.sum((old_pixels - goal_pixels) ** 2) - np.sum((new_pixels - goal_pixels) ** 2)
|
||||
#LSE
|
||||
'''
|
||||
|
||||
if line_err > max_err:
|
||||
max_err = line_err
|
||||
best_pin = test_pin
|
||||
|
||||
line_sequence.append(best_pin)
|
||||
|
||||
xs = line_cache_x[best_pin * N_PINS + pin]
|
||||
ys = line_cache_y[best_pin * N_PINS + pin]
|
||||
weight = LINE_WEIGHT * line_cache_weight[best_pin*N_PINS + pin]
|
||||
|
||||
'''
|
||||
#LSE
|
||||
old_pixels = lse_buffer[ys, xs]
|
||||
new_pixels = np.clip(old_pixels - weight, 0, 255)
|
||||
|
||||
lse_buffer[ys, xs] = new_pixels
|
||||
#LSE
|
||||
'''
|
||||
|
||||
# Subtract the line from the error
|
||||
line_mask.fill(0)
|
||||
line_mask[ys, xs] = weight
|
||||
error = error - line_mask
|
||||
error.clip(0, 255)
|
||||
|
||||
# Draw the line in the result
|
||||
cv2.line(result,
|
||||
(pin_coords[pin][0] * SCALE, pin_coords[pin][1] * SCALE),
|
||||
(pin_coords[best_pin][0] * SCALE, pin_coords[best_pin][1] * SCALE),
|
||||
color=0, thickness=4, lineType=8)
|
||||
|
||||
x0 = pin_coords[pin][0]
|
||||
y0 = pin_coords[pin][1]
|
||||
|
||||
x1 = pin_coords[best_pin][0]
|
||||
y1 = pin_coords[best_pin][1]
|
||||
|
||||
# Calculate physical distance
|
||||
dist = math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0)*(y1 - y0))
|
||||
thread_length += HOOP_DIAMETER / length * dist
|
||||
|
||||
last_pins.append(best_pin)
|
||||
pin = best_pin
|
||||
|
||||
img_result = cv2.resize(result, img.shape, interpolation=cv2.INTER_AREA)
|
||||
|
||||
diff = img_result - img
|
||||
mul = np.uint8(img_result < img) * 254 + 1
|
||||
absdiff = diff * mul
|
||||
|
||||
print(absdiff.sum() / (length * length))
|
||||
|
||||
print('\x07')
|
||||
toc = time.perf_counter()
|
||||
print("%.1f seconds" % (toc - tic))
|
||||
|
||||
cv2.imwrite(os.path.splitext(FILENAME)[0] + "-out.png", result)
|
||||
|
||||
with open(os.path.splitext(FILENAME)[0] + ".json", "w") as f:
|
||||
f.write(str(line_sequence))
|
Ładowanie…
Reference in New Issue