15.08.2015, 19:23
|
|
junior
|
|
Регистрация: 29.11.2011
Сообщений: 3,924
|
|
Paguo-86PK, вроде как запилил определение размеров указателя (кисти) и для примера нарисовал его розовым
Исходники тут.
Разумеется, писал не с нуля, поскреб по сусекам
Теперь надо этим маркером (указателем) пройти лабиринт, собирая опорные точки.
__________________
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук
|
|
15.08.2015, 22:34
|
|
Профессор
|
|
Регистрация: 16.09.2009
Сообщений: 253
|
|
Спасибо за вклад
Сообщение от nerv_
|
Paguo-86PK, вроде как запилил определение размеров указателя (кисти) и для примера нарисовал его розовым
Исходники тут.
Разумеется, писал не с нуля, поскреб по сусекам
Теперь надо этим маркером (указателем) пройти лабиринт, собирая опорные точки.
|
Спасибо
Правда, что-то слишком сложно. И мне несколько неудобно за напряг (не думал, что Вы расписывать будете код)
Просто, меня заинтересовало наличие готовых решений в этой области (как, скажем, OpenCV).
Подход с предопределением ширины кисти может быть сбойным, если в режиме реального времени с вэб-камеры эскиз лабиринта считывается с боковой стенки. Ширина кисти ближней к объективу камеры стенки лабиринта будет больше, чем дальняя, в силу законов перспективной проекции.
Сейчас я пишу (в час - по чайной ложке) скрипт (дописываю тот же залитый мной в архив), где жук оснащён радаром. Он собирает ширину и длину лучей, проходящих по пути. Двигается по наидлинейщему пути со статистически стабильной шириной. Тем самым, ширина может изменяться по мере перемещения по маршруту.
Недостаток: Крайне низкая скорость, непригодная для режима реального времени (как у Zbar).
К тому же, код пока просто выдаёт график огибающей окружения.
P.S.: По идее, чтобы задумку выдать за свою и иметь полное право на неё, я должен справиться сам.
Повторяюсь: Думал, есть готовые библиотеки (типа OpenCV).
P.P.S.: Применение: На многих форумах задумку просто осмеяли и задаваться там проблемой распознавания как-то неудобно
Так как основное применение сообществом воспринимается дегенеративным.
|
|
15.08.2015, 22:54
|
без статуса
|
|
Регистрация: 25.05.2012
Сообщений: 8,219
|
|
Сообщение от Paguo-86PK
|
Так как основное применение сообществом воспринимается дегенеративным
|
Да нет, тут скорее желание убежать от сложнозадачи, даже поиска нужных компонентов OpenCV
Посколь имел опыт с попыткой добить нечто подобное, так что nerv_, на моих глазах совершил подвиг самонапряга, правда тут есть ещё пара тройка подобных личностей, могущих себя самоувлечь и помочь
|
|
16.08.2015, 00:40
|
|
junior
|
|
Регистрация: 29.11.2011
Сообщений: 3,924
|
|
Сообщение от Paguo-86PK
|
Спасибо
|
на здоровье
Сообщение от Paguo-86PK
|
Правда, что-то слишком сложно.
|
мне кажется наоборот)
Сообщение от Paguo-86PK
|
Подход с предопределением ширины кисти может быть сбойным
|
а может и не быть)
Сообщение от Paguo-86PK
|
если в режиме реального времени с вэб-камеры эскиз лабиринта считывается с боковой стенки. Ширина кисти ближней к объективу камеры стенки лабиринта будет больше, чем дальняя, в силу законов перспективной проекции.
|
я не рассматривал задачу в таком контексте. Моя трактовка более простая.
коме того размер кисти вычисляется достаточно быстро
Сообщение от Paguo-86PK
|
По идее, чтобы задумку выдать за свою и иметь полное право на неё, я должен справиться сам
|
ну, прости
---
Сообщение от Deff
|
Посколь имел опыт с попыткой добить нечто подобное, так что nerv_, на моих глазах совершил подвиг самонапряга, правда тут есть ещё пара тройка подобных личностей, могущих себя самоувлечь и помочь
|
эм... подвиг есть не спорю, только не стоит его преувеличивать. Повторюсь, я поскреб по сусекам и, посути, надергал уже готовых модулей из своих проектов. Список всех приведен тут. Из них, под данную задачу с нуля я писал только Decoder & MatrixWalker, Rectangle, из которых последние два очевидные в своей реализации (думать не надо, только пиши).
Возвращаясь к теме
Сообщение от Paguo-86PK
|
Правда, что-то слишком сложно.
|
Лично мне проще:
- взять свой ранее написанный класс/модуль и подпилить его под себя (чем заново изобретать велосипед)
- писать на es6 с использованием классов и модулей, чем использовать процедурной подход. ООП позволяет сравнительно легко управлять сложностью приложения
а если ты посмотришь в index.js, то увидишь, что, по сути, у меня есть декодер, который всем заведует. Но, т.к. приложение не дописано, сейчас в нем не очень чисто В идеале (как-то так):
import Decoder from './modules/Decoder';
let {document} = window;
let image = document.getElementById('original');
let stage = document.getElementById('stage');
let decoder = new Decoder();
decoder.onDecodeComplete = function() {
decoder.write(stage);
};
decoder.decode(image);
куда уж проще)
__________________
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук
Последний раз редактировалось nerv_, 16.08.2015 в 00:44.
|
|
16.08.2015, 00:54
|
|
I am Student
|
|
Регистрация: 17.12.2011
Сообщений: 4,415
|
|
nerv_, на чем научился писать подобные штуки?)
__________________
Цитата:
|
Если ограничения и условия описываются как "коробка", то хитрость в том что бы найти именно коробку... Не думайте о чем то глобальном - найдите коробку.
|
|
|
16.08.2015, 09:25
|
|
Профессор
|
|
Регистрация: 16.09.2009
Сообщений: 253
|
|
Сообщение от nerv_
|
на здоровье
мне кажется наоборот)
|
Думаю, я слишком примитивно владею программированием, поэтому и кажется, что Ваш код сложен.
Для примера, набыдлокодил здесь эмулятор+дизассемблер+ассе мблер своего процессора, где можно оценить и мой стиль, и уровень владения. (звук работает в Chrome)
По теме: Я подумал, что распознание лабиринта пересекается с OCR. По сути, в этом плане мои лабиринты очень правильные: Нет оторваных стенок, нет глухих комнат, и путь всегда один (в данной версии я в концепции не развивал проход лабиринта в двух и более маршрутах).
P.S.: Кстати, мою идею осмеяли, потому что я решил строить/читать лабиринты со скрытым смыслом.
Я взял Муна, Хангыль, принцип Пентамино и составил собственный алфавит, конвертируя слова в лабиринты. (В противовес QR-коду, который без смартфона чудовищно сложно глазами прочитать)
Гласные звуки - буквы корейского алфавита.
Согласные звуки - частично шрифт Муна и греческий алфавит.
Причём, парные глухие и звонкие согласные имеют одну фигурку.
Код:
|
(Ι Β
│ Π
┌─┐
ΚΓ┌ Ω ┐MN
┌ ┴ ┐
ΣC│E┤©├A│DT
└ ┬ ┘
RL└ Υ ┘JH
└─┘
V
F) |
Один лабиринт - одно слово.
Ниже - архив с подсказками и анимациями.
Для чего это надо?
Во-первых, такие лабиринты легко печатаются на 3D-принтере. Так как имеют "правильную" структуру: Нет оторваных стен и глухих комнат.
Во-вторых, при должном навыке человек может научиться читать лабиринты за час (собственная мама в роли подопытной прочитала стихи за 15 минут). Не говоря уже о машине.
В-третьих, тактильно лабиринты можно читать пальцами при должном навыке.
Ну как? И на этом форуме идею назовёте дегенератской?
Последний раз редактировалось Paguo-86PK, 17.08.2015 в 18:36.
Причина: Добавил архив
|
|
15.05.2021, 07:36
|
|
Профессор
|
|
Регистрация: 16.09.2009
Сообщений: 253
|
|
Human Quick Response Labyrintic Text
Накoнец-то чуточку продвинулся в решении поставленной задачи.
Если раньше пытался решать её со стороны графики - сканированием изображения, что просаживало производительность.
То теперь реализован математический подход.
И можно видеть, чертя мышью лабиринт на чёрном поле, насколько отзывчиво производится его предварительная обработка в мерцающую фигуру.
<html>
<head>
<style>
body {
margin :0px 0px 0px 0px;
padding :0px 0px 0px 0px;
}
</style>
<script>
var hPprCnv;
var hPprCtx;
var hSrcCnv;
var hSrcCtx;
var hCtrCnv;
var hCtrCtx;
var hPrvCnv;
var hPrvCtx;
var hRslCnv;
var hRslCtx;
var vecs;
var circle;
var iStepFactor = 32;
// sharp
// Процедура бинарной резкости
CanvasRenderingContext2D.prototype.sharp =
function () {
var imd = this.getImageData(0, 0, this.canvas.width, this.canvas.height);
var p = imd.data;
var i = p.length,
r, g, b,
x, y, z = 384;
var mask;
while(i >= 4) {
-- i;
r = (p[-- i] >> 7) * 255; g = (p[-- i] >> 7) * 255; b = (p[-- i] >> 7) * 255;
mask = z < r + g + b ? 255 : 0;
p[i + 3] = 255, p[i + 2] = mask, p[i + 1] = mask, p[i + 0] = mask;
if(mask)
y = i >> 2;
}
x = y % this.canvas.width;
y = (y - x) / this.canvas.width;
this.putImageData(imd, 0, 0);
return {
x :x + 1,
y :y + 1
}
}
// Процедура обнаружения границ
CanvasRenderingContext2D.prototype.findEdge =
function () {
var w = this.canvas.width;
var j = i - w * 4 - 4;
var x, y, z = 384;
var r, g, b;
var k, m, r;
var dx, dy;
var minx = 1 << 30;
var miny = 1 << 30;
var maxx = 0;
var maxy = 0;
var mindy;
var maxdy;
var arr = [];
var corners = [];
var borders = [];
var tmp;
var lasta = "";
var lastx, lasty;
//
this.save();
this.globalCompositeOperation = "difference";
this.drawImage(this.canvas, 1, 1);
this.restore();
var imd = this.getImageData(1, 1, this.canvas.width, this.canvas.height);
var buf = imd.data;
var i = imd.data.length;
var pixels = new Uint32Array(imd.data.buffer);
var j = pixels.length;
var fnd = -1;
do {
fnd = Array.prototype.indexOf.call(pixels, 0xFFFFFFFF, fnd + 1);
if(fnd >= 0) {
x = fnd % this.canvas.width,
y = Math.floor(fnd / this.canvas.width);
minx = Math.min(minx, x);
miny = Math.min(miny, y);
maxx = Math.min(maxx, x);
maxy = Math.min(maxy, y);
arr.push({
x :x,
y :y,
a :""
});
}
} while(fnd >= 0);
// Сортировка пикселей контура в цепочку
for(i = 0; i < arr.length - 1; ++ i) {
x = arr[i].x;
y = arr[i].y;
k = 0;
for(j = i + 1; j < arr.length; ++ j) {
dx = arr[j].x - x, dy = arr[j].y - y;
r = Math.sqrt(dx * dx + dy * dy);
if(k == 0 || m > r)
m = r,
k = j;
}
arr.splice(i + 1, 0, arr.splice(k, 1)[0]);
}
/*var point = { x: arr[0].x, y: arr[0].y };
arr.sort((a, b) => {
var d = (a.x - point.x) ** 2 + (a.y - point.y) ** 2 - (b.x - point.x) ** 2 + (b.y - point.y) ** 2;
point = { x: b.x, y: b.y};
return d;
});*/
// Построение "маршрута" движения (векторной ориентации) линий контура
for(i = 0; i < arr.length; ++ i) {
var east = 0;
var west = 0;
var south = 0;
var north = 0;
for(j = 1; j <= iStepFactor; ++ j) {
tmp = arr[j];
dx = tmp.x - arr[0].x, dy = tmp.y - arr[0].y;
if(Math.abs(dx) > Math.abs(dy)) {
if(dx < 0)
east ++;
else
west ++;
} else
if(dy < 0)
south ++;
else
north ++;
}
var max = Math.max(east, west, north, south);
arr[0].a = max == east ? "E" : max == west ? "W" : max == north ? "N" : "S";
if(lasta != arr[0].a) {
corners.push({
x :arr[0].x,
y :arr[0].y,
a :lasta
});
if(lasta != "") {
if(!(lasta == "E" || "W" == lasta))
borders.push({
x1 :lastx,
y1 :lasty,
x2 :arr[0].x,
y2 :lasty
});
else
borders.push({
x1 :lastx,
y1 :lasty,
x2 :lastx,
y2 :arr[0].y
});
}
lastx = arr[0].x,
lasty = arr[0].y;
}
lasta = arr[0].a;
arr.push(arr.shift());
}
for(i = 0; i < corners.length - 1; ++ i) {
x = corners[i].x;
y = corners[i].y;
for(j = i + 1; j < corners.length; ++ j) {
}
}
//
corners.push({
x :arr[0].x,
y :arr[0].y,
a :lasta
});
return {
arr :arr,
corners :corners,
borders :borders
};
}
var vecmax = 0;
var vecidx = 0;
var paths = [{len: []}];
var car = {
x :0,
y :100,
dx :1,
dy :1,
dir :0.0,
cdx :0, // Counter by dx
cdy :0 // Counter by dy
};
var pen = {
x :0,
y :100
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var lapping = 0;
var race = [];
var curdir = "";
var cntdir = "";
var waydir = "";
var lastdir = "";
function run() {
var colors = {
"N" :"red",
"S" :"magenta",
"E" :"blue",
"W" :"cyan"
};
pixels.unshift(pixels.pop());
hRslCtx.clearRect(0, 0, hRslCnv.width, hRslCnv.height);
hRslCtx.beginPath();
var x, y, nx, ny;
for(i = 0; i < corners.length; ++ i) {
nx = corners[i].x, ny = corners[i].y;
if(corners[i].a == "E" || "W" == corners[i].a) {
hRslCtx.lineTo(x, ny);
} else {
hRslCtx.lineTo(nx, y);
}
dx = nx - x, dy = ny - y;
;
x = nx, y = ny;
if(i)
hRslCtx.lineTo(x, y);
else
hRslCtx.moveTo(x, y);
}
hRslCtx.fillStyle = "grey brown red orange yellow green cyan blue magenta black".split(" ")[Math.floor(Math.random() * 10)];
hRslCtx.fillStyle = "grey silver".split(" ")[Math.floor(Math.random() * 2)];
hRslCtx.closePath();
hRslCtx.fill();
//hRslCtx.stroke();
hRslCtx.beginPath();
for(i = 0; i < borders.length; ++ i) {
nx = borders[i].x1, ny = borders[i].y1;
hRslCtx.moveTo(nx, ny);
hRslCtx.lineTo(borders[i].x2, borders[i].y2);
}
hRslCtx.strokeStyle = "grey brown red orange yellow green cyan blue magenta black".split(" ")[Math.floor(Math.random() * 10)];
hRslCtx.closePath();
hRslCtx.lineWidth = 4;
hRslCtx.stroke();
return;
////////////////////////////////////////////////////////////////////////////////
}
var hqr;
var pixels;
var corners;
var borders;
function main() {
hPprCnv = document.getElementById("Paper");
hSrcCnv = document.getElementById("Source");
hCtrCnv = document.getElementById("Contour");
hPrvCnv = document.getElementById("Preview");
hRslCnv = document.getElementById("Result");
hSrcCnv.setAttribute('crossOrigin', '');
hPprCtx = hPprCnv.getContext("2d");
hCtrCtx = hCtrCnv.getContext("2d");
hSrcCtx = hSrcCnv.getContext("2d");
hPrvCtx = hPrvCnv.getContext("2d");
hRslCtx = hRslCnv.getContext("2d");
hPprCtx.fillStyle = "#FF00000";
hPprCtx.fillRect(0, 0, hPprCnv.width, hPprCnv.height);
hSrcCtx.fillStyle = "#FF000000";
hPrvCtx.fillStyle = "#CCCCCC";
hSrcCtx.fillRect(0, 0, hSrcCnv.width, hSrcCnv.height);
hPprCnv.addEventListener("mousemove",
function(e) {
x = e.offsetX;
y = e.offsetY;
if(e.buttons == 1) {
hPprCtx.fillStyle = "white";
hPprCtx.fillRect(x, y, 16, 16);
hSrcCtx.drawImage(hPprCnv, 0, 0);
localStorage.draftImage = hPprCnv.toDataURL();
Start();
}
}
);
hPprCnv.addEventListener("mouseup",
function(e) {
hSrcCtx.drawImage(hPprCnv, 0, 0);
localStorage.draftImage = hPprCnv.toDataURL();
Start();
}
);
hSrcCnv.addEventListener("mousemove",
function(e) {
x = e.offsetX;
y = e.offsetY;
if(e.buttons == 1) {
hSrcCtx.fillStyle = "white";
hSrcCtx.fillRect(x, y, 16, 16);
}
}
);
if(localStorage.draftImage) {
var im = new Image();
im.src = localStorage.draftImage;
im.addEventListener("load", function(e) {
hPprCtx.drawImage(e.target, 0, 0);
}
);
}
}
function loadDefault() {
hSrcCtx.drawImage(document.getElementsByTagName("img")[0], 0, 0, hSrcCnv.width, hSrcCnv.height);
setTimeout('Start(); hSrcCnv.style.qdisplay="none"; setInterval("run(); run(); run();", 1);', 1000);
}
function Start() {
vecs = [hSrcCtx.sharp()];
car.x = vecs[0].x;
car.y = vecs[0].y;
pen.x = car.x;
pen.y = car.y;
tmp = hSrcCtx.findEdge();
pixels = tmp.arr;
corners = tmp.corners;
borders = tmp.borders;
car.x = pixels[0].x;
car.y = pixels[0].y;
lapping = 0;
race = [];
waydir = "";
curdir = "";
cntdir = 0;
}
function Clear() {
hSrcCtx.fillStyle="black";
hSrcCtx.fillRect(0, 0, hSrcCnv.width, hSrcCnv.height);
hPprCtx.fillStyle = "black";
hPprCtx.fillRect(0, 0, hPprCnv.width, hPprCnv.height);
}
</script>
<style>
img,
canvas#Source,
canvas#Contour,
canvas#Preview {
display :none;
}
</style>
</head>
<body onload='main()'>
<button onclick='hqr.Bug()' style=display:none>Step</button>
<button onclick='Clear()'>Clear</button>
<button onclick='Start()' style=display:none>Start</button><hr />
<canvas id='Paper' width='320' height='240'></canvas><canvas id='Source' width='320' height='240'></canvas>
<canvas id='Contour' width='320' height='240' style='display:none'></canvas><canvas id='Preview' width='320' height='240'></canvas>
<canvas id='Result' width='320' height='240'></canvas>
<img style=display:none title='animanu.gif' src1='https://i.imgur.com/oZBA50h.png' src='https://i.imgur.com/NDa8KXn.gif' crossorigin = '' onload='setTimeout(loadDefault, 100)' /></img>
<div style=display:none>
<pre id='log'>---</pre>
<pre id='Log'></pre>
</div>
</body>
</html>
Последний раз редактировалось Paguo-86PK, 16.05.2021 в 04:12.
|
|
|
|