www.2527.com_澳门新葡8455手机版_新京葡娱乐场网址_
做最好的网站

H5游戏开发,图像处理

2019-04-13 11:56 来源:未知

H5游戏开发:消灭星星

2018/01/25 · HTML5 · 游戏

最初的作品出处: 坑坑洼洼实验室   

「消灭星星」是一款很经典的「消除类游戏」,它的玩法极粗略:消除相连通的同色砖块。

Web前端 1

H伍游戏开发:一笔画

2017/11/07 · HTML5 · 游戏

初稿出处: 坑坑洼洼实验室   

Web前端 2

以前做图像处理时用的是matlab,然则matlab有局地不便利支出:

一. 游戏规则

「消灭星星」存在两个版本,不过它们的平整除了「关卡分值」有些出入外,别的的条条框框都以均等的。小编介绍的版本的游戏规则整理如下:

1. 色砖遍布

  • 10 x 10 的表格
  • 5种颜色 —— 红、绿、蓝,黄,紫
  • 每类色砖个数在钦命区间内随机
  • 5类色砖在 10 x 拾 表格中私自分布

二. 排除规则

三个或七个以上同色砖块相连通便是可被拔除的砖块。

3. 分值规则

  • 化解总分值 = n * n * 5
  • 奖励总分值 = 3000 – n * n * 20

「n」表示砖块数量。上边是「总」分值的条条框框,还有「单」个砖块的分值规则:

  • 免去砖块得分值 = 拾 * i 5
  • 剩余砖块扣分值 = 40 * i 20

「i」表示砖块的索引值(从 0 开头)。简单地说,单个砖块「得分值」和「扣分值」是2个等差数列。

四. 关卡分值

关卡分值 = 一千 (level – 1) * 贰仟;「level」即眼下关卡数。

伍. 过关条件

  • 可解除色块不存在
  • 合计分值 >= 当前关卡分值

上边七个原则还要创制游戏才得以过得去。

H5游戏开发:一笔画

by leeenx on 2017-11-02

一笔画是图论[科普](https://zh.wikipedia.org/wiki/图论)中三个名扬四海的难题,它起点于柯蒙彼利埃堡7桥题材[科普](https://zh.wikipedia.org/wiki/柯尼斯堡七桥问题)。物文学家欧拉在她173六年登载的舆论《柯海法堡的七桥》中不仅消除了七桥题材,也建议了一笔画定理,顺带化解了一笔画难点。用图论的术语来说,对于一个加以的连通图[科普](https://zh.wikipedia.org/wiki/连通图)存在一条恰好含有所无线段并且没有重新的路线,那条路径正是「一笔画」。

招来连通图那条路径的经过正是「一笔画」的三1二十九日游进度,如下:

Web前端 3

  • 不开源,当时应用的本子是破解版的,至于版权难点,此处就不商讨了;
  • 其貌似只可以用于落到实处,如若完成产业化则有过多困苦;
  • 程序运行比较慢;
  • 与其他语言结合有点小意思。 当进入工作岗位之后,做的是大数额方向,接触了java与python后感觉到python对于做图像处理会越来越好,所以那里简单的对python操作图像做1些大致的牵线。

二. MVC 设计方式

小编此次又是行使了 MVC 形式来写「消灭星星」。星星「砖块」的数据结构与各个情况由 Model 已毕,游戏的主导在 Model 中达成;View 映射 Model 的更动并做出相应的行事,它的任务重点是显示动画;用户与游戏的互动由 Control 达成。

从逻辑规划上看,Model 很重而View 与 Control 很轻,可是,从代码量上看,View 很重而 Model 与 Control 相对很轻。

游玩的落到实处

「一笔画」的贯彻不复杂,小编把贯彻进程分成两步:

  1. 底图绘制
  2. 交互绘制

「底图绘制」把连通图以「点线」的样式显得在画布上,是游玩最不难实现的1些;「交互绘制」是用户绘制解题路径的经过,那一个进程会首即使处理点与点动态成线的逻辑。

  1. 率先安装pytyhon,linux系统中 已经协调带了python,至于在window系统只设置则更是简约,下载三个Anaconda直接就可以设置了,后续的模块安装则间接接纳pip安装会越发方便。在这里就不1壹讲述了。

3. Model

十 x 十 的报表用长度为 100 的数组可周到映射游戏的少数「砖块」。

[ R, R, G, G, B, B, Y, Y, P, P, R, R, G, G, B, B, Y, Y, P, P, R, R, G, G, B, B, Y, Y, P, P, R, R, G, G, B, B, Y, Y, P, P, R, R, G, G, B, B, Y, Y, P, P, R, R, G, G, B, B, Y, Y, P, P, R, R, G, G, B, B, Y, Y, P, P, R, R, G, G, B, B, Y, Y, P, P, R, R, G, G, B, B, Y, Y, P, P, R, R, G, G, B, B, Y, Y, P, P ]

1
2
3
4
5
6
7
8
9
10
11
12
[
R, R, G, G, B, B, Y, Y, P, P,
R, R, G, G, B, B, Y, Y, P, P,
R, R, G, G, B, B, Y, Y, P, P,
R, R, G, G, B, B, Y, Y, P, P,
R, R, G, G, B, B, Y, Y, P, P,
R, R, G, G, B, B, Y, Y, P, P,
R, R, G, G, B, B, Y, Y, P, P,
R, R, G, G, B, B, Y, Y, P, P,
R, R, G, G, B, B, Y, Y, P, P,
R, R, G, G, B, B, Y, Y, P, P
]

揽胜 – 灰褐,G – 深黄,B – 法国红,Y – 威尼斯红,P – 煤黑。Model 的中坚职务是以下多少个:

  • 变动砖墙
  • 免去砖块 (生成砖块分值)
  • 抓牢砖墙
  • 扫除残砖 (生成奖励分值)

底图绘制

「一笔画」是多关卡的2十二十七日游形式,小编决定把关卡(连通图)的定制以1个配备接口的样式对外揭发。对外暴光关卡接口须要有一套描述连通图形状的标准,而在作者前面有多少个选用:

  • 点记法
  • 线记法

举个连通图 —— 5角星为例来说一下那多个选项。

Web前端 4

点记法如下:

JavaScript

levels: [ // 当前关卡 { name: "伍角星", coords: [ {x: Ax, y: Ay}, {x: Bx, y: By}, {x: Cx, y: Cy}, {x: Dx, y: Dy}, {x: Ex, y: Ey}, {x: Ax, y: Ay} ] } ... ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
levels: [
// 当前关卡
{
name: "五角星",
coords: [
{x: Ax, y: Ay},
{x: Bx, y: By},
{x: Cx, y: Cy},
{x: Dx, y: Dy},
{x: Ex, y: Ey},
{x: Ax, y: Ay}
]
}
...
]

线记法如下:

JavaScript

levels: [ // 当前关卡 { name: "5角星", lines: [ {x1: Ax, y1: Ay, x2: Bx, y2: By}, {x1: Bx, y1: By, x2: Cx, y2: Cy}, {x1: Cx, y1: Cy, x2: Dx, y2: Dy}, {x1: Dx, y1: Dy, x2: Ex, y2: Ey}, {x1: Ex, y1: Ey, x2: Ax, y2: Ay} ] } ]

1
2
3
4
5
6
7
8
9
10
11
12
13
levels: [
// 当前关卡
{
name: "五角星",
lines: [
{x1: Ax, y1: Ay, x2: Bx, y2: By},
{x1: Bx, y1: By, x2: Cx, y2: Cy},
{x1: Cx, y1: Cy, x2: Dx, y2: Dy},
{x1: Dx, y1: Dy, x2: Ex, y2: Ey},
{x1: Ex, y1: Ey, x2: Ax, y2: Ay}
]
}
]

「点记法」记录关卡通关的1个答案,即端点要按一定的次第存放到数组 coords中,它是有序性的记录。「线记法」通过两点描述连通图的线条,它是冬季的笔录。「点记法」最大的优势是显示更简洁,但它必须记录四个及格答案,作者只是关卡的搬运工不是关卡创设者,所以作者最终选项了「线记法」。:)

图像打开与显示

from PIL import Image
import numpy as np
import scipy
import matplotlib.pyplot as plt
lena = Image.open('lena.jpg')           //打开图像  
print(lena.mode)                       //打印图像类型
print(lena.getpixel((0,0)))           //打印图像(0,0)处像素值
lena.show()                            //图像显示

三.一 生成砖墙

砖墙分两步生成:

  • 色砖数量分配
  • 打散色砖

辩护上,能够将 十0 个格子能够均分到 伍类颜色,可是小编玩过的「消灭星星」都不应用均分政策。通过分析三款「消灭星星」,其实能够窥见3个规律 —— 「色砖之间的数码差在二个定点的间隔内」。

倘诺把守旧意义上的均分称作「完全均分」,那么「消灭星星」的分配是一种在均分线上下波动的「不完全均分」。

Web前端 5

我把上边包车型大巴「不完全均分」称作「波动均分」,算法的切实可行达成能够瞻仰「不定均分算法」。

「打散色砖」其实便是将数组乱序的进度,作者推荐应用「 费雪耶兹乱序算法」。

以下是伪代码的兑现:

JavaScript

// 波动均分色砖 waveaverage(伍, 四, 四).forEach( // tiles 即色墙数组 (count, clr) => tiles.concat(generateTiles(count, clr)); ); // 打散色砖 shuffle(tiles);

1
2
3
4
5
6
7
// 波动均分色砖
waveaverage(5, 4, 4).forEach(
// tiles 即色墙数组
(count, clr) => tiles.concat(generateTiles(count, clr));
);
// 打散色砖
shuffle(tiles);

互动绘制

在画布上绘制路径,从视觉上便是「选用或一连连通图端点」的历程,那些进度需求缓解1个难点:

  • 手指下是不是有端点
  • 入选点到待选中式点心期间是不是成线

收集连通图端点的坐标,再监听手指滑过的坐标可以领略「手指下是或不是有点」。以下伪代码是采访端点坐标:

JavaScript

// 端点坐标音讯 let coords = []; lines.forEach(({x一, y一, x贰, y二}) => { // (x一, y1) 在 coords 数组不存在 if(!isExist(x1, y1)) coords.push([x1, y1]); // (x二, y二) 在 coords 数组不设有 if(!isExist(x2, y2)) coords.push([x2, y2]); });

1
2
3
4
5
6
7
8
// 端点坐标信息
let coords = [];
lines.forEach(({x1, y1, x2, y2}) => {
// (x1, y1) 在 coords 数组不存在
if(!isExist(x1, y1)) coords.push([x1, y1]);
// (x2, y2) 在 coords 数组不存在
if(!isExist(x2, y2)) coords.push([x2, y2]);
});

以下伪代码是监听手指滑动:

JavaScript

easel.addEventListener("touchmove", e => { let x0 = e.targetTouches[0].pageX, y0 = e.targetTouches[0].pageY; // 端点半径 ------ 取连通图端点半径的二倍,提高活动端体验 let r = radius * 2; for(let [x, y] of coords){ if(Math.sqrt(Math.pow(x - x0, 2) Math.pow(y - y0), 二) <= r){ // 手指下有端点,判断能不能够连线 if(canConnect(x, y)) { // todo } break; } } })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
easel.addEventListener("touchmove", e => {
let x0 = e.targetTouches[0].pageX, y0 = e.targetTouches[0].pageY;
// 端点半径 ------ 取连通图端点半径的2倍,提升移动端体验
let r = radius * 2;
for(let [x, y] of coords){
if(Math.sqrt(Math.pow(x - x0, 2) Math.pow(y - y0), 2) <= r){
// 手指下有端点,判断能否连线
if(canConnect(x, y)) {
// todo
}
break;
}
}
})

在未绘制任何线段或端点此前,手指滑过的任意端点都会被视作「一笔画」的开头点;在绘制了线段(或有选中式点心)后,手指滑过的端点能还是不能够与选中式点心串连成线段供给基于现有规则举办判断。

Web前端 6

上海教室,点A与点B可总是成线段,而点A与点C不可能接二连三。笔者把「能够与内定端点连接成线段的端点称作得力连接点」。连通图端点的有用连接点从连通图的线条中提取:

JavaScript

coords.forEach(coord => { // 有效连接点(坐标)挂载在端点坐标下 coord.validCoords = []; lines.forEach(({x一, y一, x二, y二}) => { // 坐标是当下线段的源点 if(coord.x === x1 && coord.y === y壹) { coord.validCoords.push([x2, y2]); } // 坐标是日前线段的顶峰 else if(coord.x === x2 && coord.y === y贰) { coord.validCoords.push([x1, y1]); } }) })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
coords.forEach(coord => {
// 有效连接点(坐标)挂载在端点坐标下
coord.validCoords = [];
lines.forEach(({x1, y1, x2, y2}) => {
// 坐标是当前线段的起点
if(coord.x === x1 && coord.y === y1) {
coord.validCoords.push([x2, y2]);
}
// 坐标是当前线段的终点
else if(coord.x === x2 && coord.y === y2) {
coord.validCoords.push([x1, y1]);
}
})
})

But…有效连接点只可以判断三个点是或不是为底图的线条,那只是多少个静态的参照,在实际的「交互绘制」中,会赶上以下情状:

Web前端 7
如上图,AB已串连成线段,当前选中式点心B的管用连接点是 A 与 C。AB 已经一而再成线,假使 BA 也串连成线段,那么线段就再也了,所以那时候 BA 不能成线,只有 AC 才能成线。

对选中式点心而言,它的立竿见影连接点有二种:

  • 与选中式点心「成线的卓有作用连接点」
  • 与选中式点心「未成线的有用连接点」

在那之中「未成线的管事连接点」才能加入「交互绘制」,并且它是动态的。

Web前端 8

回头本节内容发轫提的五个难题「手指下是还是不是有端点」 与 「选中式点心到待选中式点心之间是不是成线」,其实可统1为3个题材:手指下是不是留存「未成线的实惠连接点」。只须把监听手指滑动遍历的数组由连通图全数的端点坐标 coords 替换为如今选中式点心的「未成线的实用连接点」即可。

迄今「一笔画」的要紧成效已经落到实处。能够超越体验一下:

Web前端 9

图像类型转化convert函数

python图像处理库PIL对于PNG、BMP和JPG彩色图像格式之间的交互转换都得以通过Image模块的open()和save()函数来成功。具体说就是,在打开那一个图像时,PIL会将它们解码为三通道的“MuranoGB”图像。用户能够依据这些“普拉多GB”图像,对其展开处理。处理达成,使用函数save(),能够将处理结果保存成PNG、BMP和JPG中其余格式。那样也就成功了三种格式之间的更换。同理,别的格式的彩色图像也能够透过那种方法成就更换。当然,对于不一样格式的灰度图像,也可经过类似途径达成,只是PIL解码后是方式为“L”的图像。大家以图像Web前端 10为例,分辨率为51二x51二。

  1. 方式“帕杰罗GB”转换为任何差别方式

a. 方式“1” 格局“1”为2值图像,非黑即白。可是它各类像素用九个bit表示,0象征黑,255代提亲。上边大家将lena图像转换为“壹”图像。

lena_1 = lena.convert("1")
print(lena_1.mode)
lena_1.show()

b. 模式“L”
情势“L”为石黄图像,它的各种像素用几个bit表示,0表示黑,255象征白,别的数字代表分裂的灰度。在PIL中,从方式“LANDGB”转换为“L”情势是比照上边包车型地铁公式转换的: L = 奥迪Q3 * 299/1000 G * 587/1000 B * 11十分四00上边大家将lena图像转换为“L”图像。

lena_1 = lena.convert("L")
print(lena_1.mode)
lena_1.show()

c模式“P”
格局“P”为五位彩色图像,它的各样像素用七个bit表示,其对应的彩色值是比照调色板查询出来的。下边大家采纳暗中认可的调色板将lena图像转换为“P”图像。

lena_1 = lena.convert("P")
print(lena_1.mode)
lena_1.show()

d 方式“汉兰达GBA” 方式“TiggoGBA”为三十人彩色图像,它的各类像素用三十三个bit表示,在那之中贰四bit代表鲜黄、浅暗红和卡其色多少个通道,别的8bit表示alpha通道,即透明通道。

lena_1 = lena.convert("RGBA")
print(lena_1.mode)
lena_1.show()

e 格局“CMYK” 情势“CMYK”为3一个人彩色图像,它的每种像素用33个bit表示。情势“CMYK”正是印刷4分色形式,它是万紫千红印刷时使用的1种套色方式,利用色料的三原色混色原理,加上灰黄油墨,共计多种颜色混合叠加,形成所谓“全彩色印刷刷”。 三种标准颜色是:C:Cyan = 玫瑰紫,又称为‘士林红棕’或是‘湛蓝’M:Magenta = 品灰湖绿,又称之为‘洋深褐’;Y:Yellow = 淡紫灰;K:Key Plate(blacK) = 定位套版色(蔚蓝)。

从实例中可以识破PIL中“LacrosseGB”转换为“CMYK”的公式如下: C = 25伍 - Rubicon M = 25伍 - G Y = 25五 - B K = 0 由于该转换公式相比简单,转换后的图像颜色稍微失真。

f 情势“YCbCr” 形式“YCbCr”为二十三人彩色图像,它的每种像素用二四个bit表示。YCbCr当中Y是指亮度分量,Cb指深橙色度分量,而Cr指森林淡褐度分量。人的眸子对录像的Y分量更加灵敏,因此在通过对色度分量进行子采集样品来压缩色度分量后,肉眼将发现不到的图像品质的转变。 形式“奥迪Q三GB”转换为“YCbCr”的公式如下: Y= 0.257R 0.504G 0.098B 16 Cb = -0.148R-0.291G 0.439B 128 Cr = 0.439R-0.368G-0.071*B 128

安份守己公式,Y = 0.二五柒197 0.564111 0.09878 16= 136.877 Cb= -0.148197-0.291111 0.43978 128= 100.785 Cr = 0.439197-0.368111-0.071*7捌 128 = 16八.0玖七显而易见,PIL中永不依据这么些公式进行“奥迪Q3GB”到“YCbCr”的更换。

g 方式“I” 形式“I”为三二十一个人整型金色图像,它的每一个像素用三十一个bit表示,0象征黑,25五象征白,(0,255)之间的数字代表差异的灰度。在PIL中,从形式“库罗德GB”转换为“I”格局是安分守纪上面包车型客车公式转换的: I = Lacrosse * 299/1000 G * 587/1000 B * 114/1000

h 情势“F” 格局“F”为35位浮点深蓝图像,它的每一个像素用三10个bit表示,0代表黑,25伍意味着白,(0,255)之间的数字代表分化的灰度。在PIL中,从方式“锐界GB”转换为“F”方式是遵循下边包车型大巴公式转换的: F = 奇骏 * 299/1000 G * 587/1000 B * 114/1000

三.贰 解决砖块

「消除砖块」的平整很简短 —— 紧邻相连通相同色即能够化解

Web前端 11
前四个结合符合「相邻相连通相同色即可以祛除」,所以它们得以被免去;第伍个结合就算「相邻相同色」不过不「相衔接」所以它不可能被扫除。

「解决砖块」的同时有三个要害的职责:生成砖块对应的分值。在「游戏规则」中,作者曾经提供了相应的数学公式:「消除砖块得分值 = 10 * i 5」。

「化解砖块」算法完毕如下:

JavaScript

function clean(tile) { let count = 1; let sameTiles = searchSameTiles(tile); if(sameTiles.length > 0) { deleteTile(tile); while(true) { let nextSameTiles = []; sameTiles.forEach(tile => { nextSameTiles.push(...searchSameTiles(tile)); makeScore( count * 十 五); // 标记当前分值 deleteTile(tile); // 删除砖块 }); // 清除实现,跳出循环 if(nextSameTiles.length === 0) break; else { sameTiles = nextSameTiles; } } } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function clean(tile) {
let count = 1;
let sameTiles = searchSameTiles(tile);
if(sameTiles.length > 0) {
deleteTile(tile);
while(true) {
let nextSameTiles = [];
sameTiles.forEach(tile => {
nextSameTiles.push(...searchSameTiles(tile));
makeScore( count * 10 5); // 标记当前分值
deleteTile(tile); // 删除砖块
});
// 清除完成,跳出循环
if(nextSameTiles.length === 0) break;
else {
sameTiles = nextSameTiles;
}
}
}
}

打消的算法使用「递归」逻辑上会清晰壹些,不过「递归」在浏览器上简单「栈溢出」,所以作者未有使用「递归」达成。

电动识图

作者在录加入关贸总协定协会卡配置时,发现五个七条边以上的交接图很不难录错或录重线段。我在思维是或不是开发2个自动识别图形的插件,终归「一笔画」的图样是有平整的几何图形。

Web前端 12

上面的关卡「底图」,一眼就足以识出八个颜色:

  • 白底
  • 端点颜色
  • 线条颜色

并且那三种颜色在「底图」的面积大小顺序是:白底 > 线段颜色 > 端点颜色。底图的「采集色值表算法」很简短,如下伪代码:

JavaScript

let imageData = ctx.getImageData(); let data = imageData.data; // 色值表 let clrs = new Map(); for(let i = 0, len = data.length; i < len; i = 4) { let [r, g, b, a] = [data[i], data[i 1], data[i 2], data[i 3]]; let key = `rgba(${r}, ${g}, ${b}, ${a})`; let value = clrs.get(key) || {r, g, b, a, count: 0}; clrs.has(key) ? value.count : clrs.set(rgba, {r, g, b, a, count}); }

1
2
3
4
5
6
7
8
9
10
let imageData = ctx.getImageData();
let data = imageData.data;
// 色值表
let clrs = new Map();
for(let i = 0, len = data.length; i < len; i = 4) {
let [r, g, b, a] = [data[i], data[i 1], data[i 2], data[i 3]];
let key = `rgba(${r}, ${g}, ${b}, ${a})`;
let value = clrs.get(key) || {r, g, b, a, count: 0};
clrs.has(key) ? value.count : clrs.set(rgba, {r, g, b, a, count});
}

对此连通图来说,只要把端点识别出来,连通图的轮廓也就出去了。

图像保存

img.save('d:/lena.jpg')

注:后边的转账代码处理形式不均等,其他都以一模一样的,所以未有写代码

3.三 做实砖墙

砖墙在解除了有个别砖块后,会并发空洞,此时急需对墙体举办狠抓:

向下夯实 向左夯实 向左下夯实(先下后左)

1种高效的贯彻方案是,每便「消除砖块」后一贯遍历砖墙数组(十×十数组)再把空洞抓牢,伪代码表示如下:

JavaScript

for(let row = 0; row < 拾; row) { for(let col = 0; col < 10; col) { if(isEmpty(row, col)) { // 水平方向(向左)狠抓if(isEmptyCol(col)) { tampRow(col); } // 垂直方向(向下)抓实 else { tampCol(col); } break; } } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for(let row = 0; row < 10; row) {
for(let col = 0; col < 10; col) {
if(isEmpty(row, col)) {
// 水平方向(向左)夯实
if(isEmptyCol(col)) {
tampRow(col);
}
// 垂直方向(向下)夯实
else {
tampCol(col);
}
break;
}
}
}

But… 为了压实一个抽象对一张大数组进行全量遍历并不是一种高效的算法。在作者看来影响「墙体做实」作用的因素有:

  1. 固定空洞
  2. 砖块移动(抓实)

举目4望墙体数组的主要性指标是「定位空洞」,不过否不扫描墙体数组直接「定位空洞」?

墙体的「空洞」是出于「化解砖块」造成的,换种说法 —— 被扫除的砖头留下来的坑位便是墙体的肤浅。在「消除砖块」的同时标记空洞的岗位,那样就毫无全量扫描墙体数组,伪代码如下:

JavaScript

function deleteTile(tile) { // 标记空洞 markHollow(tile.index); // 删除砖块逻辑 ... }

1
2
3
4
5
6
function deleteTile(tile) {
// 标记空洞
markHollow(tile.index);
// 删除砖块逻辑
...
}

在地点的抓实动图,其实可以观看它的狠抓进度如下:

  1. 空泛上方的砖头向下移动
  2. 空驶列车左边的砖头向左移动

墙体在「做实」进度中,它的分界是实时在变更,假设「压实」不按实际边界举办扫描,会生出多余的空白扫描:

Web前端 13

哪些记录墙体的边际?
把墙体拆分成1个个独门的列,那么列最顶部的空白格片段正是墙体的「空白」,而任何非顶部的空白格片段即墙体的「空洞」。

Web前端 14

作者利用1组「列集合」来叙述墙体的分界并记录墙体的悬空,它的模型如下:

JavaScript

/* @ count - 列砖块数 @ start - 顶部行索引 @ end - 尾部行索引 @ pitCount - 坑数 @ topPit - 最顶部的坑 @ bottomPit - 最底部的坑 */ let wall = [ {count, start, end, pitCount, topPit, bottomPit}, {count, start, end, pitCount, topPit, bottomPit}, ... ];

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
@ count - 列砖块数
@ start - 顶部行索引
@ end - 底部行索引
@ pitCount - 坑数
@ topPit - 最顶部的坑
@ bottomPit - 最底部的坑
*/
let wall = [
{count, start, end, pitCount, topPit, bottomPit},
{count, start, end, pitCount, topPit, bottomPit},
...
];

本条模型能够描述墙体的四个细节:

  • 空列
  • 列的连日空洞
  • 列的非三番五次空洞
JavaScript

// 空列 if(count === 0) { ... } // 连续空洞 else if(bottomPit -
topPit   1 === pitCount) { ... } // 非连续空洞 else { ... }

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f3d2c2df29914802382-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f3d2c2df29914802382-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f3d2c2df29914802382-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f3d2c2df29914802382-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f3d2c2df29914802382-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f3d2c2df29914802382-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f3d2c2df29914802382-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f3d2c2df29914802382-8">
8
</div>
<div class="crayon-num" data-line="crayon-5b8f3d2c2df29914802382-9">
9
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f3d2c2df29914802382-10">
10
</div>
<div class="crayon-num" data-line="crayon-5b8f3d2c2df29914802382-11">
11
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f3d2c2df29914802382-12">
12
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f3d2c2df29914802382-1" class="crayon-line">
// 空列
</div>
<div id="crayon-5b8f3d2c2df29914802382-2" class="crayon-line crayon-striped-line">
if(count === 0) { 
</div>
<div id="crayon-5b8f3d2c2df29914802382-3" class="crayon-line">
 ...
</div>
<div id="crayon-5b8f3d2c2df29914802382-4" class="crayon-line crayon-striped-line">
}
</div>
<div id="crayon-5b8f3d2c2df29914802382-5" class="crayon-line">
// 连续空洞
</div>
<div id="crayon-5b8f3d2c2df29914802382-6" class="crayon-line crayon-striped-line">
else if(bottomPit - topPit   1 === pitCount) { 
</div>
<div id="crayon-5b8f3d2c2df29914802382-7" class="crayon-line">
 ...
</div>
<div id="crayon-5b8f3d2c2df29914802382-8" class="crayon-line crayon-striped-line">
}
</div>
<div id="crayon-5b8f3d2c2df29914802382-9" class="crayon-line">
// 非连续空洞
</div>
<div id="crayon-5b8f3d2c2df29914802382-10" class="crayon-line crayon-striped-line">
else {
</div>
<div id="crayon-5b8f3d2c2df29914802382-11" class="crayon-line">
 ...
</div>
<div id="crayon-5b8f3d2c2df29914802382-12" class="crayon-line crayon-striped-line">
}
</div>
</div></td>
</tr>
</tbody>
</table>

砖块在清除后,映射到单个列上的空洞会有二种分布形态 —— 一而再与非一连。

Web前端 15

「连续空洞」与「非一连空洞」的坚实进程如下:

Web前端 16

实质上「空驶列车」放大于墙体上,也会有「空洞」类似的分布形态 —— 再而三与非一而再。
Web前端 17

它的抓牢进度与虚无类似,那里就不赘述了。

端点识别

反驳上,通过采访的「色值表」能够平素把端点的坐标识别出来。作者设计的「端点识别算法」分以下2步:

  1. 按像素扫描底图直到遇见「端点颜色」的像素,进入第2步
  2. 从底图上拔除端点并记录它的坐标,重临继续第三步

伪代码如下:

JavaScript

for(let i = 0, len = data.length; i < len; i = 4) { let [r, g, b, a] = [data[i], data[i 1], data[i 2], data[i 3]]; // 当前像素颜色属于端点 if(isBelongVertex(r, g, b, a)) { // 在 data 中清空端点 vertex = clearVertex(i); // 记录端点消息vertexes.push(vertext); } }

1
2
3
4
5
6
7
8
9
10
for(let i = 0, len = data.length; i < len; i = 4) {
let [r, g, b, a] = [data[i], data[i 1], data[i 2], data[i 3]];
// 当前像素颜色属于端点
if(isBelongVertex(r, g, b, a)) {
// 在 data 中清空端点
vertex = clearVertex(i);
// 记录端点信息
vertexes.push(vertext);
}
}

But… 下面的算法只可以跑无损图。小编在运用了一张手机截屏做测试的时候发现,收集到的「色值表」长度为 5000 !那从来造成端点和线条的色值不能直接获得。

因而分析,能够发现「色值表」里多数色值都以看似的,也正是在原来的「采集色值表算法」的基础上添加2个近似颜色过滤即能够找出端点和线条的主色。伪代码达成如下:

JavaScript

let lineColor = vertexColor = {count: 0}; for(let clr of clrs) { // 与底色相近,跳过 if(isBelongBackground(clr)) continue; // 线段是数码第二多的水彩,端点是第二多的颜料 if(clr.count > lineColor.count) { [vertexColor, lineColor] = [lineColor, clr] } }

1
2
3
4
5
6
7
8
9
let lineColor = vertexColor = {count: 0};
for(let clr of clrs) {
// 与底色相近,跳过
if(isBelongBackground(clr)) continue;
// 线段是数量第二多的颜色,端点是第三多的颜色
if(clr.count > lineColor.count) {
[vertexColor, lineColor] = [lineColor, clr]
}
}

取到端点的主色后,再跑一遍「端点识别算法」后居识别出 20二个端点!那是干吗吧?

Web前端 18

上海教室是加大伍倍后的底图局部,石榴红端点的四周和里面充斥着大批量噪点(杂色块)。事实上在「端点识别」进程中,由于噪点的存在,把本来的端点被分解成1八个或数十二个小端点了,以下是跑过「端点识别算法」后的底图:

Web前端 19

透过上海教室,能够直观地搜查缴获一个定论:识别出来的小端点只在对象(大)端点上集中分布,并且大端点范围内的小端点叠加交错。

壹经把叠加交错的小端点归并成多个五头点,那么那些大端点将尤其近乎指标端点。小端点的合并伪代码如下:

JavaScript

for(let i = 0, len = vertexes.length; i < len - 1; i) { let vertexA = vertexes[i]; if(vertextA === undefined) continue; // 注意这里 j = 0 而不是 j = i 一 for(let j = 0; j < len; j) { let vertexB = vertexes[j]; if(vertextB === undefined) continue; // 点A与点B有增大,点B合并到点A并剔除点B if(is克罗丝(vertexA, vertexB)) { vertexA = merge(vertexA, vertexB); delete vertexA; } } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for(let i = 0, len = vertexes.length; i < len - 1; i) {
let vertexA = vertexes[i];
if(vertextA === undefined) continue;
// 注意这里 j = 0 而不是 j = i 1
for(let j = 0; j < len; j) {
let vertexB = vertexes[j];
if(vertextB === undefined) continue;
// 点A与点B有叠加,点B合并到点A并删除点B
if(isCross(vertexA, vertexB)) {
vertexA = merge(vertexA, vertexB);
delete vertexA;
}
}
}

加了小端点归并算法后,「端点识别」的准确度就上去了。经小编本地质度量试已经能够百分百 识别有损的衔接图了。

图像通道分别与联合

抑或以lena.jpg图像为例,达成图像通道分别合并的操作

from PIL import Image
import numpy as np
import scipy
import matplotlib.pyplot as plt

img = Image.open('len.jpg')
print(img.mode)
print(img.getpixel((0, 0)))
gray = img.convert("L")
r, g, b = img.split()  # 分离三通道
pic = Image.merge('RGB', (r, g, b))  # 合并三通道
plt.figure("beauty")
plt.subplot(2, 3, 1), plt.title('origin')
plt.imshow(img), plt.axis('off')
plt.subplot(2, 3, 2), plt.title('gray')
plt.imshow(gray, cmap='gray'), plt.axis('off')
plt.subplot(2, 3, 3), plt.title('merge')
plt.imshow(pic), plt.axis('off')
plt.subplot(2, 3, 4), plt.title('r')
plt.imshow(r, cmap='gray'), plt.axis('off')
plt.subplot(2, 3, 5), plt.title('g')
plt.imshow(g, cmap='gray'), plt.axis('off')
plt.subplot(2, 3, 6), plt.title('b')
plt.imshow(b, cmap='gray'), plt.axis('off')
plt.show()

结果如图所示 ![Web前端 20]

三.4 消除残砖

上一小节提到了「描述墙体的境界并记下墙体的抽象」的「列集合」,作者是直接运用那一个「列集合」来驱除残砖的,伪代码如下:

JavaScript

function clearAll() { let count = 0; for(let col = 0, len = this.wall.length; col < len; col) { let colInfo = this.wall[col]; for(let row = colInfo.start; row <= colInfo.end; row) { let tile = this.grid[row * this.col col]; tile.score = -20 - 40 * count ; // 标记奖励分数 tile.removed = true; } } }

1
2
3
4
5
6
7
8
9
10
11
function clearAll() {
let count = 0;
for(let col = 0, len = this.wall.length;  col < len; col) {
let colInfo = this.wall[col];
for(let row = colInfo.start; row <= colInfo.end; row) {
let tile = this.grid[row * this.col col];
tile.score = -20 - 40 * count ; // 标记奖励分数
tile.removed = true;
}
}
}

线条识别

作者分五个步骤完毕「线段识别」:

  1. 加以的七个端点连接成线,并搜集连线上N个「样本点」;
  2. 遍历样本点像素,如若像素色值不对等线段色值则象征这三个端点之间不设有线段

何以搜集「样式点」是个难点,太密集会影响属性;太疏松精准度不可能担保。

在作者前边有三个选用:N 是常量;N 是变量。
假设 N === 5。局地提取「样式点」如下:

Web前端 21

上航海用教室,会识别出三条线条:AB, BC 和 AC。而实际上,AC无法成线,它只是因为 AB 和 BC 视觉上共1线的结果。当然把 N 值向上升高能够缓解这些题材,但是 N 作为常量的话,那个常量的取量供给靠经验来判定,果然放任。

为了制止 AB 与 BC 同处一向线时 AC 被识别成线段,其实很简短 —— 七个「样本点」的间距小于或等于端点直径
假设 N = S / (2 * R),S 表示两点的距离,Lacrosse代表端点半径。局地提取「样式点」如下:

Web前端 22

如上航海用教室,成功地绕过了 AC。「线段识别算法」的伪代码完结如下:

JavaScript

for(let i = 0, len = vertexes.length; i < len - 1; i) { let {x: x1, y: y1} = vertexes[i]; for(let j = i 1; j < len; j) { let {x: x2, y: y2} = vertexes[j]; let S = Math.sqrt(Math.pow(x1 - x2, 2) Math.pow(y1 - y2, 2)); let N = S / (R * 2); let stepX = (x壹 - x二) / N, stepY = (y一 - y二) / n; while(--N) { // 样本点不是线段色 if(!isBelongLine(x1 N * stepX, y1 N * stepY)) break; } // 样本点都合格 ---- 表示两点成线,保存 if(0 === N) lines.push({x一, y一, x二, y2}) } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for(let i = 0, len = vertexes.length; i < len - 1; i) {
let {x: x1, y: y1} = vertexes[i];
for(let j = i 1; j < len; j) {
let {x: x2, y: y2} = vertexes[j];
let S = Math.sqrt(Math.pow(x1 - x2, 2) Math.pow(y1 - y2, 2));
let N = S / (R * 2);
let stepX = (x1 - x2) / N, stepY = (y1 - y2) / n;
while(--N) {
// 样本点不是线段色
if(!isBelongLine(x1 N * stepX, y1 N * stepY)) break;
}
// 样本点都合格 ---- 表示两点成线,保存
if(0 === N) lines.push({x1, y1, x2, y2})
}
}

几何变换

Image类有resize()、rotate()和transpose()方法进行几何变换

  • 一、图像的缩放和旋转
dst = img.resize((128, 128))
dst = img.rotate(45) # 顺时针角度表示
  • 2、转换图像
dst = im.transpose(Image.FLIP_LEFT_RIGHT) #左右互换
dst = im.transpose(Image.FLIP_TOP_BOTTOM) #上下互换
dst = im.transpose(Image.ROTATE_90)  #顺时针旋转
dst = im.transpose(Image.ROTATE_180)
dst = im.transpose(Image.ROTATE_270)

4. View

View 首要的成效有五个:

  • UI 管理
  • 映射 Model 的变化(动画)

UI 管理首倘使指「界面绘制」与「财富加载管理」,那两项成效相比较常见本文就径直略过了。View 的重头戏是「映射 Model 的转变」并完成对应的卡通。动画是复杂的,而映射的法则是差不多的,如下伪代码:

JavaScript

update({originIndex, index, clr, removed, score}) { // 还向来不 originIndex 或未有色值,直接不处理 if(originIndex === undefined || clr === undefined) return ; let tile = this.tiles[originIndex]; // tile 存在,判断颜色是或不是相同 if(tile.clr !== clr) { this.updateTileClr(tile, clr); } // 当前目录变化 ----- 表示地点也有变化 if(tile.index !== index) { this.updateTileIndex(tile, index); } // 设置分数 if(tile.score !== score) { tile.score = score; } if(tile.removed !== removed) { // 移除或添加当前节点 true === removed ? this.bomb(tile) : this.area.addChild(tile.sprite); tile.removed = removed; } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
update({originIndex, index, clr, removed, score}) {
// 还没有 originIndex 或没有色值,直接不处理
if(originIndex === undefined || clr === undefined) return ;
let tile = this.tiles[originIndex];
// tile 存在,判断颜色是否一样
if(tile.clr !== clr) {
this.updateTileClr(tile, clr);
}
// 当前索引变化 ----- 表示位置也有变化
if(tile.index !== index) {
this.updateTileIndex(tile, index);
}
// 设置分数
if(tile.score !== score) {
tile.score = score;
}
if(tile.removed !== removed) {
// 移除或添加当前节点
true === removed ? this.bomb(tile) : this.area.addChild(tile.sprite);
tile.removed = removed;
}
}

Model 的砖头每趟数据的转移都会通报到 View 的砖块,View 会依照对应的变通做相应的动作(动画)。

属性优化

鉴于「自动识图」需求对图像的的像素点进行围观,那么质量确实是个要求关怀的难点。小编设计的「自动识图算法」,在辨认图像的长河中必要对图像的像素做一回扫描:「采集色值表」 与 「采集端点」。在围观次数上实际很难下落了,可是对于一张 750 * 1334 的底图来说,「自动识图算法」需求遍历一遍长度为 750 * 1334 * 4 = 4,002,000 的数组,压力依然会有些。我是从压缩被围观数组的尺寸来提高质量的。

被扫描数组的尺寸怎么削减?
作者直接通过裁减画布的尺码来实现缩短被扫描数组尺寸的。伪代码如下:

JavaScript

// 要减小的倍数 let resolution = 4; let [width, height] = [img.width / resolution >> 0, img.height / resolution >> 0]; ctx.drawImage(img, 0, 0, width, height); let imageData = ctx.getImageData(), data = imageData;

1
2
3
4
5
// 要压缩的倍数
let resolution = 4;
let [width, height] = [img.width / resolution >> 0, img.height / resolution >> 0];
ctx.drawImage(img, 0, 0, width, height);
let imageData = ctx.getImageData(), data = imageData;

把源图片减少4倍后,获得的图纸像素数组只有原来的 4^2 = 16倍。那在性质上是相当大的升级。

图像矩阵变换

  • 在文章上半某个中最重要运用Image.open()来打开一幅图像,然后直接对这几个PIL对象实行操作。那种操作对于那一个简单的图像操作还足以,不过如若须求对图像进行商量等繁杂的操作则局限性不小。因而,日常我们加载完图片后,都以把图片转换到矩阵来展开更为复杂的操作。
  1. 开拓图像并转换为矩阵
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
img=np.array(Image.open('d:/lena.jpg'))  #打开图像并转化为数组对象
print(img)                                    #打印数组
print(img.shape)                        #大小  (512, 512, 3)
print(img.dtype)                           # 类型 uint8
print(img.size)                            #图像大小  786432
plt.figure("lena")
plt.imshow(img)
plt.axis('off')
plt.show()

假定是大切诺基GB图片,那么转换为array之后,就改成了贰个rowscolschannels的三个维度矩阵,由此,大家得以应用img[i,j,k]来访问像素值

  • 例一:打开图片,并随机添加一些椒盐噪声
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

img = np.array(Image.open('len.jpg'))

# 随机生成5000个椒盐
rows, cols, dims = img.shape
for i in range(5000):
    x = np.random.randint(0, rows)
    y = np.random.randint(0, cols)
    img[x, y, :] = 255

plt.figure("beauty")
plt.imshow(img)
plt.axis('off')
plt.show()

结果如图:Web前端 23

  • 例二:将lena图像二值化,像素值大于12八的成为壹,不然变为0
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

img = np.array(Image.open('len.jpg').convert('L'))

rows, cols = img.shape
for i in range(rows):
    for j in range(cols):
        if (img[i, j] <= 128):
            img[i, j] = 0
        else:
            img[i, j] = 1

plt.figure("lena")
plt.imshow(img, cmap='gray')
plt.axis('off')
plt.show()

结果如下图所示:Web前端 24

假诺要对三个像素点进行操作,能够使用数组切片格局访问。切片方式赶回的是以钦定间隔下标访问 该数组的像素值。上面是关于灰度图像的局地例子:

img[i,:] = im[j,:] # 将第 j 行的数值赋值给第 i 行

img[:,i] = 100 # 将第 i 列的所有数值设为 100

img[:100,:50].sum() # 计算前 100 行、前 50 列所有数值的和

img[50:100,50:100] # 50~100 行,50~100 列(不包括第 100 行和第 100 列)

img[i].mean() # 第 i 行所有数值的平均值

img[:,-1] # 最后一列

img[-2,:] (or im[-2]) # 倒数第二行

5. Control

Control 要拍卖的事情相比较多,如下:

  • 绑定 Model & View
  • 变化通过海关分值
  • 认清通过海关条件
  • 对外交事务件
  • 用户交互

初阶化时,Control 把 Model 的砖块单向绑定到 View 的砖头了。如下:

Object.defineProperties(model.tile, { originIndex: { get() {...}, set(){ ... view.update({originIndex}) } }, index: { get() {...}, set() { ... view.update({index}) } }, clr: { get() {...}, set() { ... view.update({clr}) } }, removed: { get() {...}, set() { ... view.update({removed}) } }, score: { get() {...}, set() { ... view.update({score}) } } })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Object.defineProperties(model.tile, {
    originIndex: {
        get() {...},
        set(){
            ...
            view.update({originIndex})
        }
    },  
    index: {
        get() {...},
        set() {
            ...
            view.update({index})
        }
    },
    clr: {
        get() {...},
        set() {
            ...
            view.update({clr})
        }
    },
    removed: {
        get() {...},
        set() {
            ...
            view.update({removed})
        }
    },  
    score: {
        get() {...},
        set() {
            ...
            view.update({score})
        }
    }
})
 

「通过海关分值」与「判断通过海关条件」这对逻辑在本文的「游戏规则」中有有关介绍,那里不再赘述。

对外交事务件规划如下:

name detail
pass 通关
pause 暂停
resume 恢复
gameover 游戏结束

用户交互 APIs 规划如下:

name type deltail
init method 初始化游戏
next method 进入下一关
enter method 进入指定关卡
pause method 暂停
resume method 恢复
destroy method 销毁游戏

行使「自动识图」的建议

即便作者在该地质衡量试的时候能够把富有的「底图」识别出来,可是并无法保险别的开发者上传的图纸是还是不是被很好的辨识出来。作者建议,能够把「自动识图」做为3个独自的工具使用。

小编写了一个「自动识图」的独门工具页面:
能够在那一个页不熟悉成对应的卡子配置。

直方图

6. 问题

在和讯有贰个关于「消灭星星」的话题:popstar关卡是什么样规划的?

本条话题在终极建议了3个难点 —— 「不恐怕清除和最大得分不满意过关条件的矩阵」

Web前端 25

「不能够化解的矩阵」其实就是最大得分为0的矩阵,本质上是「最大得分不知足过关条件的矩阵」。

最大得分不满意过关条件的矩阵
求「矩阵」的最大得分是三个「信封包难点」,求解的算法简单:对脚下矩阵用「递归」的样式把拥有的消灭分支都实施一遍,并取最高分值。不过javascript 的「递归」极易「栈溢出」导致算法不能实施。

骨子里在新浪的话题中提到四个缓解方案:

网上查到有先后提出做个工具随意生成关卡,自动估测计算,把适合得分条件的卡子筛选出来

本条化解方案代价是昂贵的!作者提供有源码并从未消除那一个题材,而是用3个比较取巧的法门:进入游戏前检查是事为「无法清除矩阵」,如若是重新生成关卡矩阵

注意:笔者使用的取巧方案并从未缓解难点。

结语

上边是本文介绍的「一笔画」的线上 DEMO 的二维码:

Web前端 26

游戏的源码托管在:
在那之中游戏完成的重头戏代码在:
自行识图的代码在:

谢谢耐心阅读完本小说的读者。本文仅表示小编的个人观点,如有不妥之处请不吝赐教。

感激您的翻阅,本文由 坑坑洼洼实验室Web前端, 版权全体。假使转发,请声明出处:凹凸实验室()

1 赞 1 收藏 评论

Web前端 27

壹、画灰度图直方图

绘制都足以调用matplotlib.pyplot库来展开,在这之中的hist函数能够一向绘制直方图。

调用方式:
n, bins, patches = plt.hist(arr, bins=50, normed=1, facecolor='green', alpha=0.75)
hist的参数非常多,但常用的就这五个,只有第一个是必须的,后面四个可选

arr: 需要计算直方图的一维数组

bins: 直方图的柱数,可选项,默认为10

normed: 是否将得到的直方图向量归一化。默认为0

facecolor: 直方图颜色

alpha: 透明度

返回值 :

n: 直方图向量,是否归一化由参数设定

bins: 返回各个bin的区间范围

patches: 返回每个bin里面包含的数据,是一个list

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
img=np.array(Image.open('len.jpg').convert('L'))

plt.figure("lena")
arr=img.flatten()
n, bins, patches = plt.hist(arr, bins=256, normed=1, facecolor='green', alpha=0.75)
print(n)
print(bins)
print(patches)
plt.show()

收获如下图所示:Web前端 28

7. 结语

下边是本文介绍的「消灭星星」的线上 DEMO 的2维码:

Web前端 29

打闹的源码托管在:

谢谢耐心阅读完本文章的读者。本文仅代表我的个人观点,如有不妥之处请不吝赐教。
一旦对「H伍游戏开发」感兴趣,欢迎关切大家的专栏。

贰、彩色图片直方图

实在是和灰度直方图一律的,只是个别画出三通路的直方图,然后叠加在1起。

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
src=Image.open('len.jpg')
r,g,b=src.split()

plt.figure("lena")
ar=np.array(r).flatten()
plt.hist(ar, bins=256, normed=1,facecolor='r',edgecolor='r',hold=1)
ag=np.array(g).flatten()
plt.hist(ag, bins=256, normed=1, facecolor='g',edgecolor='g',hold=1)
ab=np.array(b).flatten()
plt.hist(ab, bins=256, normed=1, facecolor='b',edgecolor='b')
plt.show()

如图: Web前端 30

参考资料

  • Knapsack problem
  • NP-completeness
  • popstar关卡是怎么样设计的?
  • 费雪耶兹乱序算法
  • 不定均分算法

    1 赞 收藏 评论

Web前端 31

TAG标签:
版权声明:本文由澳门新葡8455手机版发布于Web前端,转载请注明出处:H5游戏开发,图像处理