Node.js OpenCv图片缩放旋转
文章目录
- 前言
- 安装OpenCv
- 在package.json文件中引入opencv
- 运行例子
- 旋转图片
- 组合多种操作
- 抽象自己的矩阵操作
- 验证
- 广告一波
前言
opencv 是一个高效的视觉处理库, 可以用它来进行人脸识别等操作. 我在实践中发现用它来进行基本的图片操作速度也是远大于canvas相关的api. 速度甚至比很多 native module要快. 所以下面我就演示下如何使用 opencv 来缩放和旋转图片
安装OpenCv
安装方法参考下官方链接 这里不在赘述
在package.json文件中引入opencv
添加 “opencv”: “^7.0.0” 即可, 如果你出现了编译错误, 可以尝试替换为下面的代码. 我这里使用的 Node.js 版本为 14.0.0
"dependencies": {
"opencv": "peterbraden/node-opencv"
}
运行例子
可以运行 opencv 项目中自带 example, node_modules/opencv/examples 如果没有错误提示, 表示安装无误
旋转图片
const cv = require('opencv');
cv.readImage("./examples/files/mona.png", function(err, im){
if(err) {
console.log(err);
return;
}
// 参考文档你可以得知旋转矩阵怎么构建: https://docs.opencv.org/3.4/dd/d52/tutorial_js_geometric_transformations.html
let θ = 30 / 180 * Math.PI;
let cos = Math.cos(θ), sin = Math.sin(θ);
let floats32 = new Float32Array([cos,-sin,0,sin,cos,0]);
let m = new cv.Matrix(2, 3, cv.Constants.CV_32F);
m.put(floats32);
im.warpAffine(mat, im.width(), im.height());
im.save('./rotate-30.png');
})
平移代码类似不在举例
组合多种操作
通过文档我们可以得知, 其实 warpAffine 接受的参数是矩阵对象, 也就是说我们可以把多次旋转 & 平移操作组合为一个 Mat, 一次性应用到 im 上.
先看这个连接来回顾一下矩阵乘法 opencv 文档上的平移 & 旋转变换使用 2x3 矩阵来完成. 但 2x3 矩阵不能同 2x3 矩阵直接相乘(行列不匹配) 根据矩阵乘法, 如果一个 3x3 矩阵的样式是:
[
[1, 0, 0]
[0, 1, 0]
[0, 0, 1]
]
注意最后一行是 0,0,1 那么它无论同什么矩阵相乘得到的矩阵的最后一行都是 0,0,1 也就是说 3x3 矩阵最后一行是不变的. 所以叠加多次矩阵操作的思路就是, 多个 3x3 矩阵相乘, 最后截取为 2x3 矩阵.
抽象自己的矩阵操作
这里引入一个数学库 mathjs, 当然你也可以使用其他的数学库. 基于它封装出我们的矩阵类型
const cv = require('opencv');
const mathjs = require('mathjs');
// --------------------------
// Transform
// --------------------------
/**
* 叠加多次操作
* @constructor
*/
function Transform() {
this._mat = mathjs.matrix([
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
]);
}
/**
* 移动到某位置
* @param {number} x
* @param {number} y
* @param {number} [scale]
*/
Transform.prototype.transTo = function (x, y, scale = 1) {
this._mat = mathjs.multiply(this._mat, mathjs.matrix([
[scale, 0, x],
[0, scale, y],
[0, 0, 1],
]));
};
/**
* 旋转
* @param {number} x
* @param {number} y
* @param {number} degree 旋转角度(-90~90)
* @param {number} [scale] 缩放
*/
Transform.prototype.rotateWith = function (x, y, degree, scale = 1) {
let arc = (degree / 180) * Math.PI;
let alpha = scale * Math.cos(arc);
let beta = scale * Math.sin(arc);
this._mat = mathjs.multiply(this._mat, mathjs.matrix([
[alpha, beta, (1 - alpha) * x - beta * y],
[-beta, alpha, beta * x + (1 - alpha) * y],
[0, 0, 1],
]));
};
/**
* 获得应用到 image 上的 matrix
*/
Transform.prototype.getMat = function () {
let data = mathjs.flatten(this._mat).toArray().slice(0, -3);
let floats32 = new Float32Array(data);
let m = new cv.Matrix(2, 3, cv.Constants.CV_32F);
m.put(floats32);
return m;
};
module.exports = Transform;
验证
const Transform = require('./transform.js');
cv.readImage("./examples/files/mona.png", function(err, im){
if(err) {
console.log(err);
return;
}
let trans = new Transform();
trans.transTo(100, 50, 1);
trans.rotateWith(100, 100, 30, 1);
let mat = trans.getMat();
im.warpAffine(mat, im.width(), im.height());
im.save('./rotate-30.png');
})
代码虽然不多, 但node.js opencv的文档确实是少. 这些操作是我直接阅读 opencv c++ binding 代码结合 python 文档写出来的, 是在是呕心沥血了. 短短200行不到费了一天时间. 所以必须要广告一波