图片进行网格化,然后根据像素值求出每一个格子对应的类型,最后返回一个二维数组
npm install type-from-picture@1.0.1 -save
git clone https://github.com/522363215/TypeFromPicture.git
运行demo下的 index.html
http://blog.csdn.net/qq_22218005
这张图有一个应用就是,在海域上有一艘船,船肯定必须在水上啦,所以对船来说陆地就是船的障碍物,所以目标就是把图片进行坐标化,然后在二维坐标上标记出障碍物的位置,这样就可以在给定船一个起始和结束坐标时,可以让船避开障碍物进行移动
- 如何确定哪一个位置是陆地还是海洋
- 如何把图片进行坐标化
- 如果不光要区别分陆地、海洋,还有暴风、旋涡等多种类型
- 第一个问题,可以利用图片像素点来解决,不同的类型关键就在于像素的RGBA不同,只要我们能拿到图片中像素的数据就可以找出每一个像素所属的类型,难点就在于RGBA四个值的阈值设置。
- 第二个问题,我们可以自己设计一个比例来进行坐标化,比如1920 * 1080像素的图片,我们可以按100 * 100来进行坐标化,那么每一个格子所占的像素点数就知道啦,然后这个格子中不同类型所占的像素个数也知道,我的做法是取类型个数最多为这个格子最终的类型,但是你也可以有自己的算法来找出最终的类型(按百分比什么的度都可以)。
- 第三个问题主要就是代码层次上面的,需要代码健壮性和扩展性要强
/**
* 把图片进行网格化,然后根据像素值求出每一个格子对应的类型,最后返回一个二维数组
*/
class TypesFromPicture {
constructor(canvas, types, threshold) {
this.canvas = canvas;
this.types = types;
this.threshold = threshold;
}
/**
* [calTypesFromPixels 计算所有像素点所属的类型]
* @param {[type]} pixels [所有像素点数据]
* @return {[type]} [所有像素点所属的类型数组]]
*/
calTypesFromPixels(pixels) {
let types_data = new Array();
let len = pixels.length;
for (let i = 0; i < len; i += 4) {
types_data[i / 4] = this.calTypeFromPixel(pixels.slice(i, i + 4));
}
return types_data;
}
/**
* [calTypeFromPixel 计算一个像素点所属的类型]
* @param {[type]} pixelArray [图片的所有像素点]
* @return {[type]} [单个像素点所属的类型]
*/
calTypeFromPixel(pixelArray) {
let isBreak = false;
//默认是第一个类型值
let type = Object.keys(this.types)[0];
Object.keys(this.threshold).map((v) => {
if (!isBreak &&
pixelArray[0] >= this.threshold[v].r[0] && pixelArray[0] <= this.threshold[v].r[1] &&
pixelArray[1] >= this.threshold[v].g[0] && pixelArray[1] <= this.threshold[v].g[1] &&
pixelArray[2] >= this.threshold[v].b[0] && pixelArray[2] <= this.threshold[v].b[1] &&
pixelArray[3] >= this.threshold[v].a[0] && pixelArray[3] <= this.threshold[v].a[1]) {
type = this.types[v];
isBreak = true;
}
});
return type;
}
/**
* [gridFromTypes 网格化图片以及每个格子对应的类型]
* @param {[type]} typesData [图片每一个像素点对应类型的数组]
* @param {[type]} col [纵轴平均分成几份]
* @param {[type]} row [横轴平均分成几份]
* @return {[type]} [格式化后每个格子对应类型的一维数组]
*/
gridFromTypes(typesData, col, row) {
let XArray = new Array(),
YArray = new Array();
//~~使用: ~~1.1 === 1 、~~1.8 === 1 、~~-1.1 === -1、~~-1.8 === -1
let _col = ~~col,
_row = ~~row;
let w = this.canvas.clientWidth,
h = this.canvas.clientHeight;
let grid_w = ~~(w / _col),
grid_h = ~~(h / _row);
let grid_data = new Array();
for (let i = 0; i < _row; i++) {
for (let y = 0; y < _col; y++) {
let tmp_type = this.calGridType(typesData, y * grid_w, (y + 1) * grid_w, i * grid_h, (i + 1) * grid_h);
// grid_data[y + i * _row] = this.types[tmp_type];
YArray[y] = this.types[tmp_type];
}
XArray[i] = YArray.slice(0);
}
return XArray;
}
/**
* [calGridType 计算每个格子的类型]
* @param {[type]} typesData [转换后类型数据]
* @param {[type]} startX [格子横轴开始点]
* @param {[type]} endX [格子横轴结束点]
* @param {[type]} startY [格子纵轴开始点]
* @param {[type]} endY [格子纵轴开始点]
* @return {[type]} [计算后格子的类型]
*/
calGridType(typesData, startX, endX, startY, endY) {
let counter = {};
Object.keys(this.types).map((v) => {
counter[v] = 0;
});
for (let y = startY; y < endY; y++) {
for (let i = startX; i < endX; i++) {
Object.keys(this.types).map((v) => {
if (this.types[v] === typesData[this.canvas.clientWidth * y + i]) {
counter[v]++;
}
});
}
}
return this.calMaxInArray(counter);
}
/**
* [calMaxInArray 计算数组中最大值]
* @param {[type]} origal [保存类型数量的对象]
* @return {[type]} [数量最多的类型]
*/
calMaxInArray(origal) {
let max = -1,
type = '';
Object.keys(origal).map((v) => {
if (origal[v] > max) {
max = origal[v];
type = v;
}
});
return type;
}
}
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<p>要使用的图片:</p>
<img src="../img/海域地图.jpg" width="300" height="150" alt="tulip" id="tulip" style="margin-left:0px;" />
<p>Canvas:</p>
<canvas id="myCanvas" width="1920" height="1080" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script type="text/javascript" src="../TypeFromPicture.js"></script>
<script type="text/javascript" src="./test.js"></script>
</body>
</html>
test.js
//自定义类型
const types = {
land: 0,
ocean: 1
};
//自定义阈值
const threshold = {
land: {
r: [23, 43],
g: [48, 68],
b: [88, 120],
a: [0, 255],
},
ocean: {
r: [65, 100],
g: [130, 180],
b: [180, 220],
a: [0, 255],
}
};
//等img加载完成后再进行canvas绘制,否则拿不到数据
document.getElementById("tulip").onload = function() {
//在canvas上绘制图片
let c = document.getElementById("myCanvas");
let ctx = c.getContext("2d");
let img = document.getElementById("tulip");
ctx.drawImage(img, 0, 0);
//获取图片一些信息
let data = ctx.getImageData(0, 0, ctx.canvas.clientWidth, ctx.canvas.clientHeight).data;
let typesFromPicture = new TypesFromPicture(ctx.canvas, types, threshold);
let types_data = typesFromPicture.calTypesFromPixels(data);
//坐标化成100*100
let result = typesFromPicture.gridFromTypes(types_data, 100, 100);
console.log(JSON.stringify(result));
};
//下面加上我封装的类,我这里就不写啦
Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.
--args --disable-web-security --user-data-dir