Комиксатор
Судьба распорядилась так, что в последние пару месяцев я активно работал с OpenCV библиотекой.
Библиотека конечно шикарная, дает множество возможностей. Я правда, к сожалению, наверно не освоил и одного процента ее, но для моего проекта решить задачу, хоть и топорно, но удалось
Пока я изучал возможности этого проекта, пришлось перечитать немало информации, и вот один из сайтов в интернете мне хорошо помог в понимании -
http://robocraft.ru/page/opencv/.
Среди статей там нашлась интересная идея как картинку преобразовать в вариант комикса.
Захотел повторить реализацию, немного изменив условия - сделать это с помощью javascript у себя на сайте, при этом все вычисления будут происходить в браузере пользователя
Исходный код с комментариями привожу ниже. Саму библиотеку opencv.js можно либо скомпилировать самому (инструкция для 4.1.2, например, здесь -
https://docs.opencv.org/4.1.2/d4/da1/tutorial_js_setup.html), либо просто забрать отсюда ;)
Download file src.jsconst OPENCV_URL = '/js/opencv.js'; var OPENCV_LOADED = false; // OpenCV загружаем так чтобы проконтролировать процесс загрузки, ведь все вычисления можно производить только после того как он загружен. // Размер opencv.js достаточно большой, поэтому надо сделать так чтобы он не мешал загрузке самой страницы - грузим его только если OpenCV понадобится loadOpenCv = function(onloadCallback) { let script = document.createElement('script'); script.setAttribute('async', ''); script.setAttribute('type', 'text/javascript'); script.addEventListener('load', () => { if (cv.getBuildInformation) { console.log(cv.getBuildInformation()); onloadCallback(); } else { // WASM cv['onRuntimeInitialized']=()=>{ console.log(cv.getBuildInformation()); onloadCallback(); } } OPENCV_LOADED = true; }); script.addEventListener('error', () => { console.log('Failed to load ' + OPENCV_URL); }); script.src = OPENCV_URL; let node = document.getElementsByTagName('script')[0]; node.parentNode.insertBefore(script, node); }; // Это основная функция обработки изображения mainCV = function() { let src = cv.imread('imageCanvas'); // читаем загруженную картинку let gray = new cv.Mat(); cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY, 0); // переводим в градации серого let canny = new cv.Mat(); cv.Canny(gray, canny, 10, 100, 3, false); // находим границы Canny let rgbaPlanes = new cv.MatVector(); cv.split(src, rgbaPlanes); // разбиваем исходную картинку на каналы R G B let R = rgbaPlanes.get(0); let G = rgbaPlanes.get(1); let B = rgbaPlanes.get(2); // получаем адреса отдельных каналов R G B cv.subtract(R, canny, R); // вычитаем из каждого канала границу которую получили в сером. Если вычислать границы каждого канала, cv.subtract(G, canny, G); // и затем вычитать ее из этого же канала, то это приводит к такому инетересному явлению, что, cv.subtract(B, canny, B); // если эта граница появляется только на одном канале, то линии "комиксные" будут разноцветные. А лучше все же воспринимаются серые let dst = new cv.Mat(); cv.merge(rgbaPlanes, dst); // собираем картинку обратно, в новый Mat cv.imshow('imageComics', dst); // отображаем его как результат src.delete(); // подчищаем за собой dst.delete(); rgbaPlanes.delete(); R.delete(); G.delete(); B.delete(); } function handleImage(e){ var reader = new FileReader(); reader.onload = function(event){ var img = new Image(); img.onload = function(){ canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img,0,0); if(OPENCV_LOADED) { mainCV(); } else { loadOpenCv(mainCV); } } img.src = event.target.result; } reader.readAsDataURL(e.target.files[0]); }