返回顶部
首页 > 资讯 > 前端开发 > JavaScript >OpenCV.js实现乔丹动图素描效果图文教程
  • 173
分享到

OpenCV.js实现乔丹动图素描效果图文教程

OpenCV.js乔丹动图素描效果OpenCV.js动图素描 2022-11-13 14:11:59 173人浏览 薄情痞子
摘要

目录背景技术OpenCV.js 优点OpenCV.js 地址项目搭建准备图片1. 引入 OpenCV.js查看 OpenCV.js 引入状态2. 读取图片并显示3. 彩色图片转成灰度

背景

大家都知道,最近几年大热的ai(人工智能),并且使用AI做人脸识别和物品的分类,其实AI不光可以做这些基本操作,还可以用其来画素描,因为本人是乔丹的篮球粉丝,于是想用AI的技术来实现乔老爷子素描。

技术

因为本人是前端程序猿 爱好 AI,所以我会用前端和AI的方式来实现乔老爷子素描。正好OpenCV.js可以满足我们的需求。

OpenCV.js 优点

OpenCV.js 的出现使得 javascript 开发者可以高效便捷的使用 OpenCV 提供的图形处理算法,也就是说开发者仅凭借浏览器就能快速开发诸如图片风格美化、图像识别、OCR等功能的应用。

OpenCV.js 地址

文档:docs.opencv.org/4.x/index.h…

GitHubgithub.com/opencv/open…

闲话不多说,今天就让我们跟着乔老爷子一起用OpenCV实现素描效果吧!

项目搭建

准备图片

1. 引入 OpenCV.js

可以直接如下引入,也可以下载到本地,再引入:

<script src="https://docs.opencv.org/4.x/opencv.js"></script>

查看 OpenCV.js 引入状态

代码如下:

// html
<p id="status">OpenCV.js is loading...</p>
// js
let Module = {
  onRuntimeInitialized() {
    document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
  }
};
Module.onRuntimeInitialized();

效果,当页面的 loading 变成 read ,说明已完成OpenCV.js加载。

2. 读取图片并显示

html 代码如下:

<div>
  <div class="inputoutput">
    <img id="imageSrc" alt="No Image" width="100%" />
    <div class="caption">imageSrc <input type="file" id="fileInput" name="file" /></div>
  </div>
  <div class="inputoutput">
    <canvas id="canvasOutput" ></canvas>
    <div class="caption">canvasOutput</div>
  </div>
</div>

js 代码如下:

let imgElement = document.getElementById('imageSrc');
let inputElement = document.getElementById('fileInput');
inputElement.addEventListener('change', (e) => {
  imgElement.src = URL.createObjectURL(e.target.files[0]);
}, false);
imgElement.onload = function() {
  let img_origin = cv.imread(imgElement);
  cv.imshow('canvasOutput', img_origin);
  img_origin.delete();
};

效果如下图:

然后点击上传图片,上传图片后如下显示:

稍微解释一下上面的代码,首先我们可以本地上传一个图片,通过fileInput获取图片文件,并把图片传给imageSrc渲染,

然后我们利用cv.imread('demo.jpg')读取了这张图片,保存到img_origin这个变量里面。

接下来用cv.imshow('origin', img_origin)将这张照片通过一个canvas显示出来,并且这个窗口的名称叫做canvasOutput

3. 彩色图片转成灰度图

接下来我们要把彩色图片转换成灰度图:

function cvtColor(img_origin) {
  let img_gray = new cv.Mat();
  cv.cvtColor(img_origin, img_gray, cv.COLOR_RGBA2GRAY, 0);
  return img_gray;
}

没错,将彩色RGB图片转换成灰度图用cv.cvtColor(img_origin, img_gray, cv.COLOR_RGBA2GRAY, 0); 就可以啦。

但是要注意这里我们用的是cv.cvtColor方法,它的cv.COLOR_RGBA2GRAY传参。

上面这段代码执行后,效果如下:

4. 对灰度图进行高斯模糊

接下来让我们对这张灰度图进行高斯模糊:

function GaussianBlur(img_origin) {
  let img_blurred = new cv.Mat();
  let ksize = new cv.Size(5, 5);
  cv.GaussianBlur(img_origin, img_blurred, ksize, 0);
  return img_blurred;
}

在这里,我们用cv.GaussianBlur(img_origin, img_blurred, ksize, 0)完成了图像的高斯模糊。

在这里我们使用的(5,5)参数就表示高斯核的尺寸,这个核尺寸越大图像越模糊。但是记住尺寸得是奇数!这是为了保证中心位置是一个像素而不是四个像素。

什么高斯模糊?

模糊就是一种特殊的滤波,经过这种滤波后图像变得不清晰。我们知道滤波 = 原始图像和掩膜的卷积,当掩膜(窗口)服从高斯分布时,此时我们称这种滤波为高斯滤波,也称为高斯模糊。

这样我们就得到一个模糊的乔老爷子:

5. 图像二值化

接下来到关键的一步啦!让我们对这张模糊过的图片进行二值化:

function adaptiveThreshold(img_origin) {
  let img_threshold = new cv.Mat();
  cv.adaptiveThreshold(img_origin, img_threshold, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 5, 2);
  return img_threshold;
}

二值化的概念其实很简单,就是对一张图片上的点,像素值大于等于某个值的都直接设为最大值,小于这个值的都直接设为最小值,这样这张图片上每个点都只可能是最大值或最小值其中之一了,其中我们比较的这个数值就是阈值。

运行后就可以得到一个二值化的乔老爷子:

6.再次对二值化图像进行模糊

function img(img_origin, img_target) {
  let img_gray = cvtColor(img_origin);
  let ksize1 = new cv.Size(5, 5);
  let img_blurred1 = GaussianBlur(img_gray, ksize1);
  let img_threshold1 = adaptiveThreshold(img_blurred1);
  let img_blurred2 = GaussianBlur(img_threshold1, ksize1);
  img_target = img_blurred2;
  cv.imshow('canvasOutput', img_target);
}

和上面写的一样我们用cv.GaussianBlur()完成了高斯模糊,这样我们就可以得到一个模糊的描边乔老爷子,如下显示:

7.再次进行二值化

接下来我们对这张图片再次进行二值化:

function img(img_origin, img_target) {
  let img_gray = cvtColor(img_origin);
  let ksize1 = new cv.Size(5, 5);
  let img_blurred1 = GaussianBlur(img_gray, ksize1);
  let img_threshold1 = adaptiveThreshold(img_blurred1);
  let img_blurred2 = GaussianBlur(img_threshold1, ksize1);
  let img_threshold2 = threshold(img_blurred2);
  img_target = img_threshold2;
  cv.imshow('canvasOutput', img_target);
}

8.图像开运算

下面让我们去掉图片中一些细小的噪点,这种效果可以通过图像的开运算来实现:

function bitwise_not(img_origin) {
  let img_opening = new cv.Mat();
  let M = new cv.Mat();
  let ksize = new cv.Size(3, 3);
  M = cv.getStructuringElement(cv.MORPH_CROSS, ksize);
  cv.morphologyEx(img_origin, img_opening, cv.MORPH_GRADIENT, M);
  return img_opening;
}

要理解图像的开运算就要知道图像的腐蚀和膨胀,所谓的图像腐蚀就是如下的操作,类似于把一个胖子缩小一圈变瘦的感觉:

图像膨胀就是腐蚀的反向操作,把图像中的区块变大一圈,把瘦子变成胖子。

因此当我们对一个图像先腐蚀再膨胀的时候,一些小的区块就会由于腐蚀而消失,再膨胀回来的时候大块区域的边线的宽度没有发生变化,这样就起到了消除小的噪点的效果。图像先腐蚀再膨胀的操作就叫做开运算。

这样下来我们就可以实现对一张彩色图片转换成素描的效果啦!

看到这里恭喜大家你已经完成了70%了,下面我们要玩高级一点做动图。

10.读取并处理视频中的图像

搞定了单张图片,对视频进行处理就非常简单了,只需要将视频里每一帧都做同样的处理再输出即可。

首先在开头位置加上读取视频的语句:

let video = document.getElementById('videoInput');
 let cap = new cv.VideoCapture(video);
 let frame = new cv.Mat(video.height, video.width, cv.CV_8UC4);
  let fgmask = new cv.Mat(video.height, video.width, cv.CV_8UC1);

然后创建一个setTimeout定时任务,将图像处理的语句都放进去通过上面的方法处理成图片,并通过canvasOutput渲染出来。

最后完整代码如下:

html 代码:

<div>
  <div class="control"><button id="startAndStop" disabled>Start</button></div>
  <div class="inputoutput">
    <video id="videoInput" width="320" height="240" src="./mp4/7.mp4"></video>
    <div class="caption">imageSrc <input type="file" id="fileInput" name="file" /></div>
  </div>
  <div class="inputoutput">
    <canvas id="canvasOutput" ></canvas>
    <div class="caption">canvasOutput</div>
  </div>
</div>

js 代码: 首先要变量声明

let streaming = false;
let videoInput = document.getElementById('videoInput');
let startAndStop = document.getElementById('startAndStop');
let canvasOutput = document.getElementById('canvasOutput');
let canvasContext = canvasOutput.getContext('2d');

代码监听和控制

startAndStop.addEventListener('click', () => {
    if (!streaming) {
        videoInput.play().then(() => {
            onVideoStarted();
        });
    } else {
        videoInput.pause();
        videoInput.currentTime = 0;
        onVideoStopped();
    }
});
function onVideoStarted() {
    streaming = true;
    startAndStop.innerText = 'Stop';
    videoInput.height = videoInput.width * (videoInput.videoHeight / videoInput.videoWidth);
    video()
}
function onVideoStopped() {
    streaming = false;
    canvasContext.clearRect(0, 0, canvasOutput.width, canvasOutput.height);
    startAndStop.innerText = 'Start';
}
videoInput.addEventListener('canplay', () => {
    startAndStop.removeAttribute('disabled');
});

主要渲染代码:

function video() {
let video = document.getElementById('videoInput');
  let cap = new cv.VideoCapture(video);
  let frame = new cv.Mat(video.height, video.width, cv.CV_8UC4);
  let fgmask = new cv.Mat(video.height, video.width, cv.CV_8UC1);
  const FPS = 30;
  function processVideo() {
      try {
          if (!streaming) {
              // clean and stop.
              frame.delete(); fgmask.delete();
              return;
          }
          let begin = Date.now();
          // start processing.
          cap.read(frame);
          img(frame, fgmask);
          // cv.imshow('canvasOutput', fgmask);
          // schedule the next one.
          let delay = 1000/FPS - (Date.now() - begin);
          setTimeout(processVideo, delay);
      } catch (err) {
          console.log(err);
      }
  };
  // schedule the first one.
  setTimeout(processVideo, 0);
}

原图:

效果如下:

Markup

<p id="status">OpenCV.js is loading...</p>
<div>
  <div class="control"><button id="startAndStop" disabled>Start</button></div>
  <div class="inputoutput">
    <video id="videoInput" width="300" src="./mp4/7.mp4"></video>
    <img id="imageSrc" alt="No Image" width="100%"/>
    <div class="caption">imageSrc <input type="file" id="fileInput" name="file" /></div>
  </div>
  <div class="inputoutput">
    <canvas id="canvasOutput" ></canvas>
    <div class="caption">canvasOutput</div>
  </div>
</div>

script

let streaming = false;
let videoInput = document.getElementById('videoInput');
let startAndStop = document.getElementById('startAndStop');
let canvasOutput = document.getElementById('canvasOutput');
let canvasContext = canvasOutput.getContext('2d');
let imgElement = document.getElementById('imageSrc');
let inputElement = document.getElementById('fileInput');
inputElement.addEventListener('change', (e) => {
  imgElement.src = URL.createObjectURL(e.target.files[0]);
}, false);
imgElement.onload = function() {
  let img_origin = cv.imread(imgElement);
  let img_target = new cv.Mat();
  img(img_origin, img_target);
  // cv.imshow('canvasOutput', img_origin);
  img_origin.delete();
  img_target.delete();
};
function img(img_origin, img_target) {
  let img_gray = cvtColor(img_origin);
  let ksize1 = new cv.Size(5, 5);
  let img_blurred1 = GaussianBlur(img_gray, ksize1);
  let img_threshold1 = adaptiveThreshold(img_blurred1);
  let img_blurred2 = GaussianBlur(img_threshold1, ksize1);
  let img_threshold2 = threshold(img_blurred2);
  let img_opening = bitwise_not(img_threshold2);
  let ksize2 = new cv.Size(3, 3);
  let img_opening_blurred = GaussianBlur(img_opening, ksize2);
  img_target = img_opening_blurred;
  cv.imshow('canvasOutput', img_target);
  // img_origin.delete();
}
function cvtColor(img_origin) {
  let img_gray = new cv.Mat();
  cv.cvtColor(img_origin, img_gray, cv.COLOR_RGBA2GRAY, 0);
  return img_gray;
}
function GaussianBlur(img_origin, ksize) {
  let img_blurred = new cv.Mat();
  // let ksize = new cv.Size(5, 5);
  cv.GaussianBlur(img_origin, img_blurred, ksize, 0);
  return img_blurred;
}
function adaptiveThreshold(img_origin) {
  let img_threshold = new cv.Mat();
  cv.adaptiveThreshold(img_origin, img_threshold, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 5, 2);
  return img_threshold;
}
function threshold(img_origin) {
  let img_threshold = new cv.Mat();
  cv.threshold(img_origin, img_threshold, 200, 255, cv.THRESH_BINARY);
  return img_threshold;
}
function bitwise_not(img_origin) {
  let img_opening = new cv.Mat();
  let M = new cv.Mat();
  let ksize = new cv.Size(3, 3);
  M = cv.getStructuringElement(cv.MORPH_CROSS, ksize);
  cv.morphologyEx(img_origin, img_opening, cv.MORPH_GRADIENT, M);
  return img_opening;
}
function video() {
  let video = document.getElementById('videoInput');
  let cap = new cv.VideoCapture(video);
  let frame = new cv.Mat(video.height, video.width, cv.CV_8UC4);
  let fgmask = new cv.Mat(video.height, video.width, cv.CV_8UC1);
  const FPS = 30;
  function processVideo() {
      try {
          if (!streaming) {
              // clean and stop.
              frame.delete(); fgmask.delete();
              return;
          }
          let begin = Date.now();
          // start processing.
          cap.read(frame);
          img(frame, fgmask);
          // cv.imshow('canvasOutput', fgmask);
          // schedule the next one.
          let delay = 1000/FPS - (Date.now() - begin);
          setTimeout(processVideo, delay);
      } catch (err) {
          console.log(err);
      }
  };
  // schedule the first one.
  setTimeout(processVideo, 0);
}
startAndStop.addEventListener('click', () => {
    if (!streaming) {
        videoInput.play().then(() => {
            onVideoStarted();
        });
    } else {
        videoInput.pause();
        videoInput.currentTime = 0;
        onVideoStopped();
    }
});
function onVideoStarted() {
    streaming = true;
    startAndStop.innerText = 'Stop';
    videoInput.height = videoInput.width * (videoInput.videoHeight / videoInput.videoWidth);
    video()
}
function onVideoStopped() {
    streaming = false;
    canvasContext.clearRect(0, 0, canvasOutput.width, canvasOutput.height);
    startAndStop.innerText = 'Start';
}
videoInput.addEventListener('canplay', () => {
    startAndStop.removeAttribute('disabled');
});
let Module = {
  // Https://emscripten.org/docs/api_reference/module.html#Module.onRuntimeInitialized
  onRuntimeInitialized() {
    document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
  }
};
Module.onRuntimeInitialized();

结语

其实很简单,大家可以自己实操,最后说几个我遇见的问题:

  • OpenCV.js文件比较大,解决方法:本地、cdn。
  • canvas渲染视频需要服务环境,解决方法:node.js

以上就是OpenCV.js实现乔丹动图素描效果图文教程的详细内容,更多关于OpenCV.js乔丹动图素描效果的资料请关注编程网其它相关文章!

--结束END--

本文标题: OpenCV.js实现乔丹动图素描效果图文教程

本文链接: https://lsjlt.com/news/171730.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • OpenCV.js实现乔丹动图素描效果图文教程
    目录背景技术OpenCV.js 优点OpenCV.js 地址项目搭建准备图片1. 引入 OpenCV.js查看 OpenCV.js 引入状态2. 读取图片并显示3. 彩色图片转成灰度...
    99+
    2022-11-13
    OpenCV.js乔丹动图素描效果 OpenCV.js动图素描
  • Android实现图片拖动效果
    要求: 1.通过手指移动来拖动图片  2.控制图片不能超出屏幕显示区域 技术点: 1.MotionEvent处理 2.对View进行动态定位(layout) acti...
    99+
    2022-06-06
    图片 动效 Android
  • iOS实现图片抖动效果
    本文实例为大家分享了iOS实现图片抖动效果的具体代码,供大家参考,具体内容如下 效果图: 核心代码: // // ViewController.m // 图标抖动 // // ...
    99+
    2022-05-28
    iOS 图片抖动
  • JavaScript实现长图滚动效果
    本文实例为大家分享了JavaScript之长图滚动的具体代码,供大家参考,具体内容如下 长图的滚动会涉及定时器: 我们先来回顾下定时器: <!DOCTYPE html&g...
    99+
    2024-04-02
  • java实现动态图片效果
    本文实例为大家分享了java实现动态图片效果,供大家参考,具体内容如下 源码 package forGame; import javax.imageio.ImageIO; im...
    99+
    2024-04-02
  • VC++6.0实现直线扫描转换的图文教程
    目录一、添加菜单栏二、添加对话框三、建立消息映射四、编写函数总结一、添加菜单栏 使用VC++6.0新建一个空白文档后,双击【Menu】下的【ID_MAINFRAME】,添加一个子菜单...
    99+
    2023-01-12
    vc++ 直线扫描转换 visualc++ vc++画线
  • 使用CSS实现响应式图片自动轮播效果的教程
    随着移动设备的普及,网页设计需要考虑到不同终端的设备分辨率和屏幕尺寸等因素,以实现良好的用户体验。在实现网站的响应式设计时,常常需要使用到图片轮播效果,以展示多张图片在有限的可视窗口中的内容,同时也能够增强网站的视觉效果。本文将介绍如何使用...
    99+
    2023-11-21
    图片轮播 响应式 CSS
  • Android编程实现图标拖动效果的方法
    本文实例讲述了Android编程实现图标拖动效果的方法。分享给大家供大家参考,具体如下: 最近优化图标拖动时的速率,稍微有一点点效果,直接把代码贴出来,有兴趣一起讨论的朋友可以...
    99+
    2022-06-06
    方法 动效 Android
  • iOS实现图片自动切换效果
    本文实例为大家分享了iOS实现图片自动切换的具体代码,供大家参考,具体内容如下 #import "ViewController.h" #define ImageViewCount...
    99+
    2022-05-24
    iOS 图片切换
  • Android实现图片左右滑动效果
    关于滑动效果,在Android中用得比较多,本示例实现的滑动效果是使用ViewFlipper来实现的,当然也可以使用其它的View来实现。接下来就让我们开始实现这种效果。 接...
    99+
    2022-06-06
    图片 动效 Android
  • jQuery如何实现图片滑动效果
    这篇文章主要为大家展示了“jQuery如何实现图片滑动效果”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“jQuery如何实现图片滑动效果”这篇文章吧。思路:当鼠...
    99+
    2024-04-02
  • JS实现图片自动播放效果
    本文实例为大家分享了JS实现图片自动播放效果的具体代码,供大家参考,具体内容如下 JS实现图片自动播放 1、先看效果图 2、完整代码 <!DOCTYPE html>...
    99+
    2024-04-02
  • jquery实现图片自动轮播效果
    本文实例为大家分享了jquery实现图片自动轮播效果的具体代码,供大家参考,具体内容如下 HTML代码如下: <div id="container">         ...
    99+
    2024-04-02
  • AndroidFlutter实现图片滑动切换效果
    目录前言SlideTransition 介绍示例效果实现总结前言 我们开始来介绍转换类的动画组件,实际上这类转换动画组件也可以自己通过 AnimatedBuilder&nb...
    99+
    2024-04-02
  • JavaScript怎么实现长图滚动效果
    小编给大家分享一下JavaScript怎么实现长图滚动效果,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!JavaScript是什么JavaScript是一种直译式的脚本语言,其解释器被称为JavaScript引擎,是浏览器...
    99+
    2023-06-14
  • java如何实现动态图片效果
    这篇文章主要介绍java如何实现动态图片效果,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!源码package forGame;import javax.imageio.ImageIO;import...
    99+
    2023-06-14
  • Android实现探探图片滑动效果
    之前一段时间,在朋友的推荐下,玩了探探这一款软件,初玩的时候,就发现,这款软件与一般的社交软件如陌陌之类的大相径庭,让我耳目一新,特别是探探里关于图片滑动操作让人觉得非常新鲜。所以在下通过网上之前的前辈的经历加上自己的理解,也来涉涉水。下面...
    99+
    2023-05-31
    android 图片滑动 roi
  • css3怎么实现鼠标经过图片显示描述的动画效果
    本篇内容主要讲解“css3怎么实现鼠标经过图片显示描述的动画效果”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“css3怎么实现鼠标经过图片显示描述的动画效果”吧...
    99+
    2024-04-02
  • win8设备管理器扫描硬件改动图文教程
      1、第一次启动电脑,桌面图标只有回收站,在桌面空白处点击个性化,如下图所示: 面图标,如下图所示:   3、勾选计算机,点击确定,如下图所示:   4、在桌面上找到“计算机”图标,右键选...
    99+
    2023-06-02
    win8 设备管理器 扫描硬件改动 硬件 图文
  • 怎么用marquee元素实现滚动字体与图片的效果
    这篇文章主要介绍了怎么用marquee元素实现滚动字体与图片的效果,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。   marquee元素可...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作