скелетизация изображения на js
Пытаюсь реализовать алгоритм ZS + OPTA скелетизации изображения. Функция должна принимать изображение и выдавать результат скелетизации.
Я пытался сделать такой алгоритм для бинарного массива, затем картинку переводить в ч/б , делать из нее массив (черный пиксель = 1, белый = 0) и применять функцию ZhangSuen() к этому массиву, а потом как-нибуль выводить это в canvas.
Но у меня возникли проблемы с ZhangSuen(), работает правильно, но удаляет много лишнего. Раз на маленьком массиве есть сбои, то на больших изображениях скорее всего будет работать очень плохо.
Основным предназначением любого алгоритма скелетизации является максимальное утоншиение всех линий на бинарном изображении, что приводит к получению скелета, в котором все линии имеют толщину в один пиксель.
Может кто-то подскажет как реализовать этот алгоритм немного по другому, обойдя шаг перевода в массив или найдет ошибку в коде ниже
вот что получилось (Буква R без палочки)
Целая программа должна работать так
Алгоритм
код алгоритма ZS+OPTA
function Point(x, y) {
this.x = x;
this.y = y;
}
let ZhangSuen = (function () {
function ZhangSuen() {
}
ZhangSuen.image = [
'00000000000000000000000000000000',
'01111111110000000111111110000000',
'01110001111000001111001111000000',
'01110000111000001110000111000000',
'01110001111000001110000000000000',
'01111111110000001110000000000000',
'01110111100000001110011111110000',
'01110011110000001111001111000000',
'01110001111000000111111110000000',
'00000000000000000000000000000000'
];
ZhangSuen.nbrs = [[0, -1], [1, -1], [1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1]];
ZhangSuen.nbrGroups = [[[0, 2, 4], [2, 4, 6]], [[0, 2, 6], [0, 4, 6]]];
ZhangSuen.toWhite = new Array();
ZhangSuen.main = function (args) {
ZhangSuen.grid = new Array(ZhangSuen.image.length);
for (let r = 0; r < ZhangSuen.image.length; r++)
ZhangSuen.grid[r] = (ZhangSuen.image[r]).split('');
ZhangSuen.thinImage();
};
ZhangSuen.thinImage = function () {
let firstStep = false;
let hasChanged;
do {
hasChanged = false;
firstStep = !firstStep;
for (let r = 1; r < ZhangSuen.grid.length - 1; r++) {
for (let c = 1; c < ZhangSuen.grid[0].length - 1; c++) {
if (ZhangSuen.grid[r][c] !== '1')
continue;
let nn = ZhangSuen.numNeighbors(r, c);
if (nn < 2 || nn > 6)
continue;
if (ZhangSuen.numTransitions(r, c) !== 1)
continue;
if (!ZhangSuen.atLeastOneIsWhite(r, c, firstStep ? 0 : 1))
continue;
ZhangSuen.toWhite.push(new Point(c, r));
hasChanged = true;
}
}
for (let i = 0; i < ZhangSuen.toWhite.length; i++) {
let p = ZhangSuen.toWhite[i];
ZhangSuen.grid[p.y][p.x] = '0';
}
ZhangSuen.toWhite = new Array();
} while ((firstStep || hasChanged));
ZhangSuen.printResult();
};
ZhangSuen.numNeighbors = function (r, c) {
let count = 0;
for (let i = 0; i < ZhangSuen.nbrs.length - 1; i++)
if (ZhangSuen.grid[r + ZhangSuen.nbrs[i][1]][c + ZhangSuen.nbrs[i][0]] === '1')
count++;
return count;
};
ZhangSuen.numTransitions = function (r, c) {
let count = 0;
for (let i = 0; i < ZhangSuen.nbrs.length - 1; i++)
if (ZhangSuen.grid[r + ZhangSuen.nbrs[i][1]][c + ZhangSuen.nbrs[i][0]] === '0') {
if (ZhangSuen.grid[r + ZhangSuen.nbrs[i + 1][1]][c + ZhangSuen.nbrs[i + 1][0]] === '1')
count++;
}
return count;
};
ZhangSuen.atLeastOneIsWhite = function (r, c, step) {
let count = 0;
let group = ZhangSuen.nbrGroups[step];
for (let i = 0; i < 2; i++)
for (let j = 0; j < group[i].length; j++) {
let nbr = ZhangSuen.nbrs[group[i][j]];
if (ZhangSuen.grid[r + nbr[1]][c + nbr[0]] === '0') {
count++;
break;
}
}
return count > 1;
};
ZhangSuen.printResult = function () {
for (let i = 0; i < ZhangSuen.grid.length; i++) {
let row = ZhangSuen.grid[i];
console.log(row.join(''));
}
};
return ZhangSuen;
}());
ZhangSuen.main(null);




