基于节点编辑的openCV

引子:

opencv学习梯度不算很平坦,其中一个很重要的原因就是cv复杂度,cv中函数其特定的使用场景,对不同函数使用的参数不同,结果会差距很大。cv虽然提供了快速gui接口,也可以用cvui直接渲染控件到界面上,简单的任务基本应付。但对一些复杂算法,除了需要大量的方法、参数微调之外,还需要有记录和分析对比等一系列数据报告。前一段时间在学习Blender的节点系统,参数调节所见即所得非常直观,工作效率大为提升。试用一段时间后感觉可以在opencv中搞点动作,用最直观的方法来开发opencv功能。

但在节点系统选型上,纠结了很久,imgui-node-editor是第一选择,因为它依赖少,跨平台性很好,渲染上显示上没有太大的缺陷,但是没有节点计算逻辑涉及,就是说自己去实现节点计算逻辑,缺乏较好的应用案例,这也是我最终放弃imgui-node-editor的一个主要原因。

Litegraph.js 是基于javascript的,互交以canvas为基础的,原生节点拿来可就用。以此为基础,可以实现大部分界面互交。虽然关于litegraph.js开发的资料比较少,但是litegraph.js本体就是一个很好的学习案例,可以参考如shader节点、MIDI处理等模块,webglstudio.js-master也非常值得参考。另外litegraph.js也是支持节点计算和节点拓展功能。自定义节点写起来比较清晰,我后面会花一点时间来介绍。

js和python接口:EEL是一个可选项,在python程序中标注下接口,即可以通过js来访问该接口以此来执行python函数,同样道理,也可以在Web中标注函数接口,让python来调用该接口进行页面更新。EEL可以作为一个服务器后台程序运行也可以一个Chrome独立程序直接运行。

图像接口:基础对象如数值、字符串等都可以方便的传输,图像稍微麻烦点,传输需要用到base64格式作为中间格式往来于各个节点之间,python接口在收到base64格式的参数后,转换成为np的array格式后即可进行后续处理,计算结束后再以base64格式返回js。

举个简单例子:

常规方法

比如需要对图像做一个消除孤立点的工作,所需的python代码如下,代码需要对图像做几步操作,打开原始图像,一次dilate,一次Canny,一次connectedComponentsWithStats,最后根据阈值进行孤立点涂色或标注等等,最后输出图像,代码如下

import cv2
import numpy as np
filename="image/test.jpg"
img = cv2.imread(filename,0)
kernel = np.ones((3,3),np.uint8)
dilate = cv2.dilate(img,kernel,iterations = 1)
canny1=cv2.Canny(dilate,100,200)
_, labels, stats, centroids = cv2.connectedComponentsWithStats(canny1)
for istat in stats:
    if istat[4]<120:
        if istat[3]>istat[4]:
            r=istat[3]
        else:r=istat[4]
        cv2.rectangle(canny1,tuple(istat[0:2]),tuple(istat[0:2]+istat[2:4]) , 0,thickness=-1) 
cv2.imwrite('canny1.jpg',canny1)

我们的方法

通过节点实现的,开发的界面如下
在这里插入图片描述

一个典型节点的例子,旋转图片,有两个控制
在这里插入图片描述

第一个参数控制图像旋转的角度,用拖动控制角度的大小
第二个参数控制图像是否需要保留裁剪(图像原始大小会改变)

节点功能划分不一定非常科学,所以按颜色、输入输出、变换、算子来划分。已经实现的Opencv节点如下,

颜色
名称简介
rgbrgb三色
hsvhsv三色
hsv2rgb颜色系统转换
rgb2hsv颜色系统转换
输入
名称简介
image文件图像输入
camera摄像机输入
weburlWeb文件视频输入
输出
名称简介
Preview图像显示
Save image图像保存

图像变换

名称简介
rotate旋转
scale拉伸
shape获取大小
Split分色
Merge合色
connectedComponents联通区域

图像算子

名称简介
Threshold旋转
Canny拉伸
Gauss高斯模糊
Erode腐蚀
Dilate和膨胀
Filter筛选

标注

名称简介
Hist直方图
Drawstr标注文本(不支持中文)
Rect标注矩形
Line标注线形

集合操作

名称简介
集合
each对每个对象操作

Litegraph节点接口实现

为了实现对opencv图像功能的封装,除了需要进行节点初始化外,还自定义节点包括如下内容:
输入/输出节点、属性、节点部件(用于具体参数的控制或其他)
添加输入节点,第一个参数是节点名,第二个参数是节点类型,类型不同不允许连接

输入输出

//初始化阶段
this.addInput("A","number");
//添加输入节点,调用方式同输入节点
this.addOutput("A+B","number");
//其次需要实现liteGraph Node几个重要接口,最后要注册自己的节点

节点执行

//OnExecute阶段
//获得第一个输入节点的内容
this.getInputData(0);
//输出值到第一个输出节点
this.setOutputData(0,value);
//获得数据
A = getInputData(0)
//做点什么
var B = do_something(A)
//返回数据
setOutputData(0,B)

节点属性

属性很简单,是可以被序列化的内部变量,结构也不难

    this.properties = {
        kernel_size: 5,
        iterate_times: 1,
        kernel_type:"MORPH_RECT",
   };

执行

Litegraph.js 会根据连接结构,自动计算节点优先级,决定哪些节点先计算,哪些节点后计算。这里只需要把每个节点的”任务”安排好,坐享其成即可,看上去是不是很简单。
OnExecute接口在graph.runStep() 或graph.run() 执行时会被调用,runStep()是进行指定有限数量,run则是不断进行计算直到接受到stop()信号,在计算负荷较重的场景,建议使用runStep()替代run()

节点高级内容

节点部件

包括基本的组件:
slider 、text、button都是一致的

text输入的时候会出现一个输入框,在触屏的环境没有试验过,这个困扰了我很久很久,到最后才发现是css引用出了问题,eel调试js还是要用chrome的日志来解决,这和js调试是一致的,

在这里插入图片描述
slider 滑轨
在这里插入图片描述

combo 类似于下拉框
在这里插入图片描述
toggle 类似于checkbox
在这里插入图片描述

缺点:

没有color picker ,slider仅支持浮点,但都不是大问题,对于自定义节点绘图,使用此接口进行二次开发,当节点塌陷的情况不需要执行,静态为主的节点可以绘制到image对象中减少2d重绘,背景绘制可以参考如下代码

onDrawBackground()= function(){
  if(this.flags.collapsed)
    return;
  ctx.save();
  ctx.fillColor = "black";
  ctx.fillRect(0,0,10,this.size[1]);
  ctx.restore();
  }

类似的js问题还有不少,

比如that和this的问题,js老鸟都懂的

还有比如 EEL返回值的问题

之前是这样想的,结果一直取不到,折腾了将近一个礼拜

var B = eel.color_filter(A,low,upper);
this.setOutputData(0,B);

后来只有阅读eel的源码,知道eel返回的是一个promise对象,因为以前做js异步编程的机会比较少,没真正学透造成的

var B = eel.color_filter(A,low,upper);

B().then(
	(data)=>{
		this.setOutputData(0,data);
	},
	(err)=>	{this.setOutputData(0,"data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=");}
)

这三个接口仅在需要处理用户互交的场景使用,比如绘图、标记等,可以参考源码CurveEditor部分来学习。

onMouseDown()
onMouseMove()
onMouseUp()

在这里插入图片描述

效果展示

在这里插入图片描述
gitee似乎没法上传了,github代码地址如下
https://github.com/touchmaker/opencv-node-edit

eel_run.py     <-- Python scripts
switch.py
web/           <-- Web folder
  test01.jpg
  test02.jpg
  test03.jpg
  main.html
  css/
    menu.css
  js/
    litegraph.core.js
    comm.js
    curvenode.js
    nodecv.js
    menu.js

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a0v5K2uH-1664352261858)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/28deadf63c214db7bca1b7a96b1a83a7~tplv-k3u1fbpfcp-zoom-1.image)]

文章写到这里,觉得屏幕前的自己比较磨叽

项目从开始到现在陆陆续续做了2个月左右,自己写的代码绝没有超过2000行,能用如此精简的代码实现该需求,自己也感到不可思议,

我只做代码的搬运工,自然也没有必要孤芳自赏,稍微修改后转投github,希望有兴趣的同志一起加入,维护和issue问题,提出想法

很多同学现在的也在磨刀霍霍准备跳槽,不管是做前端的还是后台猴子,能坚守在一线的程序员越来越少了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值