Android图像处理,消灭星星

H5游戏开发:消灭星星

2018/01/25 · HTML5 ·
游戏

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

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

www.301.net 1

原先做图像处理时用的是matlab,可是matlab有一些不方便人民群众支出:

图像色彩处理


Android对图片的拍卖,经常使用的数据结构便是位图—Bitmap,它富含了一张图纸的有所数据。整个图片都以由点阵和颜色值组成的,点阵正是一个富含像素的矩阵,每二个要素对应着图片的贰个像素,颜色值—AENVISIONGB,分别对应光滑度、红、绿、蓝那三个通道分量,它们一起决定了各类像素点显示的颜色,对图纸的色彩处理实际上便是对这几个像素点的大路分量做调整。

在情调解和处理理中,日常从七个角度来讲述3个图像。

  • 色彩:色彩的总体协助
  • 饱和度:颜色的纯度,从0(灰)到百分之百(饱和)来实行描述
  • 亮度:颜色的相持明暗程度

在android中,系统应用二个颜色矩阵—ColorMatrix,来拍卖图像的那个色彩效果。Android中颜色矩阵是3个4×5的矩阵,它用来对图片的情调实行拍卖。而对于各样像素点,都有三个颜色分量矩阵用来保存颜色的安德拉GBA值,如下图所示:

www.301.net 2

水彩矩阵A

www.301.net 3

各类像素点的颜色分量矩阵C

在处理图像时,使用矩阵乘法运算AC来处理颜色分量矩阵,如下图所示:

www.301.net 4

矩阵乘法运算

算算进程:
     R1 = a * R + b * G + c * B + d * A + e;
     G1 = f * R + g * G + h * B + i * A + j;
     B1 = k * R + l * G + m * B + n * A + o;
     A1 = p * R + q * G + r * B + s * A + t;

能够窥见,对于颜色矩阵A是按以下措施划分的:
    * 第壹行的a b c d e值决定新的颜色值中的福特Explorer分量—蓝灰
    * 第①行的f g h i j值决定新的颜色值中的G分量—铁锈色
    * 第①行的k l m n o值决定新的颜色值中的B分量—浅紫蓝
    * 第④行的p q r s t值决定新的颜色值中的A分量—发光度
    * 矩阵A中的第⑥列—e j o t值分别控制每一种分量中的offset,即偏移量

想要对原图片进行颜色的调动,就要求安装好用于调整颜色的矩阵A

www.301.net 5

原始矩阵

平时有三种方法:
① 、改变偏移量
将矩阵A的第④列的值进行修改,即改变颜色的偏移量,别的值保持初叶矩阵的值

www.301.net 6

改变颜色偏移量

  原图片每种像素点的矩阵天青和肉桂色的水彩分量都增添了100,红绿混合为淡铁锈棕,最后会使得整张图片偏黄。
② 、改变颜色周详
修改颜色分量中的有个别周全值,别的值照旧维持初步矩阵的值

www.301.net 7

变动颜色全面

  矩阵运算后,原图片每一种像素点的矩阵雪白的水彩分量会化为原来的两倍,最后使得原图片的颜色偏绿。

1. 游戏规则

「消灭星星」存在五个本子,可是它们的规则除了「关卡分值」有个别出入外,别的的平整都以平等的。小编介绍的版本的游戏规则整理如下:

1. 色砖分布

  • 10 x 10 的表格
  • 5种颜色 —— 红、绿、蓝,黄,紫
  • 每类色砖个数在钦点区间内任意
  • 5类色砖在 10 x 10 表格中自由分布

2. 清除规则

五个或四个以上同色砖块相连通便是可被扫除的砖头。

3. 分值规则

  • 铲除总分值 = n * n * 5
  • 奖励总分值 = 三千 – n * n * 20

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

  • 扫除砖块得分值 = 10 * i + 5
  • 剩余砖块扣分值 = 40 * i + 20

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

4. 关卡分值

关卡分值 = 1000 + (level – 1) * 3000;「level」即当前关卡数。

5. 过关条件

  • 可免除色块不存在
  • 一共分值 >= 当前关卡分值

上边七个标准化还要创立游戏才得以过得去。

  • 不开源,当时采取的版本是破解版的,至于版权难点,此处就不探讨了;
  • 其貌似只好用于落到实处,若是完成产业化则有不少不便;
  • 程序运转相比慢;
  • 与其它语言结合有点小标题。
    当进入工作岗位之后,做的是大数据方向,接触了java与python后感到python对于做图像处理会越发好,所以那边简单的对python操作图像做一些简短的介绍。
更改色光属性

系统封装了3个类—ColorMatrix,通过那几个类,能够很便宜地经过改变矩阵值来拍卖颜色效果(色调、饱和度、亮度)。本质上是一个一维数组[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t]。

www.301.net 8

ColorMatrix

调剂色调(色彩的团团转运算):
  ColorMatrix类提供了setRotate(int axis, float
degrees)来调节颜色的色彩。第一个参数,使用0、一 、2来代表Red、格林、Blue三种颜色的拍卖,第③个参数,正是内需处理的值。

    /**
     * Set the rotation on a color axis by the specified values.
     * <p>
     * <code>axis=0</code> correspond to a rotation around the RED color
     * <code>axis=1</code> correspond to a rotation around the GREEN color
     * <code>axis=2</code> correspond to a rotation around the BLUE color
     * </p>
     */
    public void setRotate(int axis, float degrees) {
        reset();
        double radians = degrees * Math.PI / 180d;
        float cosine = (float) Math.cos(radians);
        float sine = (float) Math.sin(radians);
        switch (axis) {
        // Rotation around the red color
        case 0:
            mArray[6] = mArray[12] = cosine;
            mArray[7] = sine;
            mArray[11] = -sine;
            break;
        // Rotation around the green color
        case 1:
            mArray[0] = mArray[12] = cosine;
            mArray[2] = -sine;
            mArray[10] = sine;
            break;
        // Rotation around the blue color
        case 2:
            mArray[0] = mArray[6] = cosine;
            mArray[1] = sine;
            mArray[5] = -sine;
            break;
        default:
            throw new RuntimeException();
        }
    }

调节饱和度
  通过色彩的移动运算单独增强Tucson,G,B的饱和度,ColorMatrix类提供了setSaturation(float
sat)方法来全体调节图像的饱和度,参数代表设置颜色饱和度的值,当饱和度为0时,图像成为灰度图像,数值越大图像越饱和。

    /**
     * Set the matrix to affect the saturation of colors.
     *
     * @param sat A value of 0 maps the color to gray-scale. 1 is identity.
     */
    public void setSaturation(float sat) {
        reset();
        float[] m = mArray;

        final float invSat = 1 - sat;
        final float R = 0.213f * invSat;
        final float G = 0.715f * invSat;
        final float B = 0.072f * invSat;

        m[0] = R + sat; m[1] = G;       m[2] = B;
        m[5] = R;       m[6] = G + sat; m[7] = B;
        m[10] = R;      m[11] = G;      m[12] = B + sat;
    }

调节亮度(色彩的缩放运算)
  当三原色以同样的百分比举行混合的时候,就会显得出湖蓝,使用那几个规律来改变八个图像的亮度,亮度为0时,图像成为全黑。ColorMatrix类提供setScale(float
rScale, float gScale, float bScale, float
aScale)方法来调节颜色的亮度值。

    /**
     * Set this colormatrix to scale by the specified values.
     */
    public void setScale(float rScale, float gScale, float bScale,
                         float aScale) {
        final float[] a = mArray;

        for (int i = 19; i > 0; --i) {
            a[i] = 0;
        }
        a[0] = rScale;
        a[6] = gScale;
        a[12] = bScale;
        a[18] = aScale;
    }

2. MVC 设计情势

小编这一次又是行使了 MVC
情势来写「消灭星星」。星星「砖块」的数据结构与各样情况由 Model
实现,游戏的宗目的在于 Model 中成就;View 映射 Model
的更动并做出相应的一颦一笑,它的天职首假诺体现动画;用户与娱乐的互相由
Control 完毕。

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

  1. 首先安装pytyhon,linux系统中
    已经本身带了python,至于在window系统只设置则越是简明,下载3个Anaconda一向就足以设置了,后续的模块安装则直接接纳pip安装会越发便于。在此地就不一一讲述了。
一些常用的图像颜色处理矩阵
  • 灰度效果

www.301.net 9

灰度矩阵

www.301.net 10

灰度效果

  • 图像反转

www.301.net 11

图像反转矩阵

www.301.net 12

图像反转效果

  • 忆旧效果

www.301.net 13

怀旧矩阵

www.301.net 14

忆旧效果

  • 去色效果

www.301.net 15

去色矩阵

www.301.net 16

去色效果

3. Model

10 x 10 的表格用长度为 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
]

PRADO – 高粱红,G – 珍珠白,B – 深橙,Y – 海洋蓝,P – 棕红。Model
的主干义务是以下两个:

  • 转移砖墙
  • 解除砖块 (生成砖块分值)
  • 狠抓砖墙
  • 消除残砖 (生成奖励分值)

图像打开与展示

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()                            //图像显示
像素点分析

能够由此转移各种像素点的切实可行ALX570GB值,来达到拍卖一张图像效果的指标。系统提供了Bitmap.getPixel()方法来博取某些像素点,也提供了Bitmap.getPixels()方法来提取整个Bitmap中的像素点,并保存到3个数组中:
getPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height)
当获得到现实的颜色值之后,就能够通过相应的算法来修改它的ALX570GB值,从而重构到一张新的图像。

常用图像像素点处理作用

—底片效果:

B.r = 255 - B.r; 
B.g = 255 - B.g; 
B.b = 255 - B.b;

www.301.net 17

底片效果

—老照片效果:

r1 = (int) (0.393 * r + 0.769 * g + 0.189 * b);
g1 = (int) (0.349 * r + 0.686 * g + 0.168 * b);
b1 = (int) (0.272 * r + 0.534 * g + 0.131 * b);

www.301.net 18

老照片效果

—浮雕效果:

B.r = C.r - B.r + 127;
B.g = C.g - B.g + 127;
B.b = C.b - B.b + 127;

www.301.net 19

浮雕效果

3.1 生成砖墙

砖墙分两步生成:

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

理论上,能够将 100 个格子能够均分到 5
类颜色,可是作者玩过的「消灭星星」都不行使均分政策。通过分析五款「消灭星星」,其实能够窥见八个规律
—— 「色砖之间的多少差在三个固定的间隔内」。

假若把古板意义上的均分称作「完全均分」,那么「消灭星星」的分配是一种在均分线上下波动的「不完全均分」。

www.301.net 20

小编把上面的「不完全均分」称作「波动均分」,算法的现实贯彻可以参见「兵连祸结均分算法」。

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

以下是伪代码的完结:

JavaScript

// 波动均分色砖 waveaverage(5, 4, 4).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);

图像类型转化convert函数

python图像处理库PIL对于PNG、BMP和JPG彩色图像格式之间的竞相转换都足以通过Image模块的open()和save()函数来成功。具体说正是,在开拓那些图像时,PIL会将它们解码为三通道的“哈弗GB”图像。用户能够根据这一个“奥迪Q3GB”图像,对其举办处理。处理完结,使用函数save(),能够将处理结果保存成PNG、BMP和JPG中任何格式。那样也就成功了二种格式之间的转移。同理,别的格式的彩色图像也得以通过那种艺术落成更换。当然,对于区别格式的灰度图像,也可透过类似途径达成,只是PIL解码后是形式为“L”的图像。大家以图像www.301.net 21为例,分辨率为512×512。

  1. 情势“奥迪Q5GB”转换为别的不一样形式

a. 方式“1”
形式“1”为二值图像,非黑即白。不过它各种像素用九个bit表示,0象征黑,255代提亲。下边大家将lena图像转换为“1”图像。

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

b. 模式“L”
形式“L”为赫色图像,它的每种像素用7个bit表示,0意味着黑,255意味白,其余数字代表不一致的灰度。在PIL中,从形式“EnclaveGB”转换为“L”情势是服从上面的公式转换的:
L = 奥迪Q5 * 299/1000 + G *www.301.net, 587/1000+ B * 114/一千上面大家将lena图像转换为“L”图像。

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

c模式“P”
方式“P”为8个人彩色图像,它的种种像素用七个bit表示,其相应的彩色值是根据调色板查询出来的。上面大家利用暗许的调色板将lena图像转换为“P”图像。

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

d 形式“CRUISERGBA”
方式“PAJEROGBA”为三11人彩色图像,它的各类像素用三11个bit表示,在那之中24bit代表天青、铅灰和花青几个通道,别的8bit表示阿尔法通道,即透明通道。

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

e 方式“CMYK”
方式“CMYK”为叁十几位彩色图像,它的种种像素用三十二个bit表示。情势“CMYK”便是印刷伍分色方式,它是五颜六色印刷时使用的一种套色格局,利用色料的三原色混色原理,加上紫紫水晶色油墨,共计八种颜色混合叠加,形成所谓“全彩色印刷刷”。
三种标准颜色是:C:Cyan = 靛青,又称之为‘深灰白色’或是‘湛蓝’M:Magenta =
品天灰,又称为‘洋冰雪蓝’;Y:Yellow = 深橙;K:Key Plate(blacK) =
定位套版色(紫水晶色)。

从实例中得以查出PIL中“KoleosGB”转换为“CMYK”的公式如下: C = 255 – XC60 M = 255 –
G Y = 255 – B K = 0 由于该转换公式比较简单,转换后的图像颜色稍微失真。

f 方式“YCbCr”
格局“YCbCr”为2二位彩色图像,它的每种像素用23个bit表示。YCbCr当中Y是指亮度分量,Cb指深孔雀蓝色度分量,而Cr指浅紫铜色色度分量。人的双眼对录像的Y分量更灵敏,由此在经过对色度分量举办子采集样品来压缩色度分量后,肉眼将发现不到的图像质量的变型。
格局“TiggoGB”转换为“YCbCr”的公式如下: Y= 0.257R+0.504G+0.098B+16 Cb =
-0.148
R-0.291G+0.439B+128 Cr = 0.439R-0.368G-0.071*B+128

依据公式,Y = 0.257197+0.564111+0.09878+16= 136.877 Cb=
-0.148
197-0.291111+0.43978+128= 100.785 Cr =
0.439197-0.368111-0.071*78+128 = 168.097
不问可知,PIL中不要依照那些公式实行“普拉多GB”到“YCbCr”的转移。

g 格局“I”
方式“I”为三十一位整型红棕图像,它的每一种像素用33个bit表示,0表示黑,255象征白,(0,255)之间的数字代表分裂的灰度。在PIL中,从情势“奥迪Q5GB”转换为“I”情势是遵守上边包车型地铁公式转换的:
I = 奥迪Q5 * 299/1000 + G * 587/1000 + B * 114/1000

h 情势“F”
方式“F”为叁十一个人浮点深草绿图像,它的每种像素用三十一个bit表示,0象征黑,255象征白,(0,255)之间的数字代表不一样的灰度。在PIL中,从情势“TiggoGB”转换为“F”情势是比照下边包车型地铁公式转换的:
F = 路虎极光 * 299/1000+ G * 587/1000 + B * 114/1000

图表变换处理


Android系统对于图像的图样变换也是经过矩阵来进展处理的,种种像素点都发表了其坐标的X、Y新闻,用于图形变换的矩阵是2个3×3的矩阵:

www.301.net 22

图片变换矩阵A

www.301.net 23

像素点坐标矩阵C

行使变换矩阵去处理每3个像素点的时候,与颜色矩阵的矩阵乘法一样:
    X1 = a * X + b * Y + c
    Y1 = d * X + e * Y + f
     1 = g * X + h * Y + i
平日状态下,会让g = h = 0,i = 1,那样就使1 = g * X + h * Y +
i恒成立。
与色彩变换矩阵的上马矩阵一样,图形变换矩阵也有1个方始矩阵:

www.301.net 24

图形变换初步矩阵

3.2 消除砖块

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

www.301.net 25
前七个结合符合「相邻相连通相同色即能够解除」,所以它们能够被解决;第5个结合就算「相邻相同色」不过不「相联接」所以它不可能被扫除。

「消除砖块」的还要有三个主要的职务:生成砖块对应的分值。在「游戏规则」中,作者曾经提供了相应的数学公式:「消除砖块得分值
= 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 * 10 +
5); // 标记当前分值 deleteTile(tile); // 删除砖块 }); //
清除达成,跳出循环 if(next山姆eTiles.length === 0) break; else {
sameTiles = next萨姆eTiles; } } } }

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;
}
}
}
}

扫除的算法使用「递归」逻辑上会清晰一些,可是「递归」在浏览器上不难「栈溢出」,所以作者没有运用「递归」完成。

图像保存

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

注:前边的转折代码处理方式不雷同,别的都以千篇一律的,所以没有写代码

图像的变形处理平常包蕴以下为主转移
  • 平移变换
    平移变换的坐标值变换进程就是将各种像素点都实行平移变换,从P(x0,y0)平移到P(x1,y1):

www.301.net 26

平移变换

矩阵变换:

www.301.net 27

平移变换矩阵

  • 旋转变换
    旋转变换即指一个点围绕壹在那之中央旋转到1个新的点。当从P(x0,y0)点,以坐标原点O为旋转核心旋转到P(x,y)时,

www.301.net 28

旋转变换

可以获得:

x0 = r*cosα 
y0 = r*sinα 
x = r*cos(α+θ) = r*cosα*cosθ − r*sinα*sinθ = x0*cosθ − y0*sinθ 
y = r*sin(α+θ) = r*sinα*cosθ + r*cosα*sinθ = y0*cosθ + x0*sinθ

矩阵变换如下:

www.301.net 29

旋转矩阵变换

上述是以坐标原点为旋转宗旨举办旋转变换,若是以任意点O为旋转中央来开始展览旋转变换,平常供给以下多个步骤:
  壹 、将坐标原点平移到O点
  二 、使用前面讲的以坐标原点为着力的转动格局开始展览旋转变换
  三 、将坐标原点还原

  • 缩放变换
    像素点是不设有缩放的定义,不过由于图像是由许三个像素点组成的,借使将每一个点的坐标都开展相同期比较例的缩放,最终就会形成让整个图像缩放的效果
    x1 = K1 * x0
    y1 = K2 * y0

www.301.net 30

缩放矩阵变换

  • 错切变换
    错切变换是一种比较万分的线性别变化换,错切变换的职能正是让全部点的X坐标(也许Y坐标)保持不变,而相应的Y坐标(或许X坐标)则按百分比发生位移,且活动的大大小小和该点到Y轴(也许X轴)的相距成正比。错切变换平日包涵三种——水平错切与垂直错切。
    水平错切:
    x1 = x0 + K1 * y0
    y1 = y0

www.301.net 31

水平错切

www.301.net 32

水平错切矩阵变换

笔直错切:
  x1 = x0
  y1 = K2 * x0 + y0

www.301.net 33

笔直错切

www.301.net 34

垂直错切矩阵变换

3.3 抓好砖墙

砖墙在拔除了一些砖头后,会晤世空洞,此时必要对墙体举行坚实:

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

一种高效的达成方案是,每一遍「消除砖块」后一直遍历砖墙数组(10×10数组)再把空洞做实,伪代码表示如下:

JavaScript

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; } } }

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. 砖块移动(抓好)

扫描墙体数组的显要目的是「定位空洞」,但能或无法不扫描墙体数组直接「定位空洞」?

墙体的「空洞」是出于「消除砖块」造成的,换种说法 ——
被解除的砖头留下来的坑位正是墙体的悬空。在「消除砖块」的还要标记空洞的职位,那样就不用全量扫描墙体数组,伪代码如下:

JavaScript

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

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

在地点的压实动图,其实能够看来它的压实进程如下:

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

墙体在「压实」进度中,它的边界是实时在变化,假使「抓好」不按实际边界进行扫描,会发出多余的空域扫描:

www.301.net 35

什么记录墙体的疆界?
把墙体拆分成1个个单身的列,那么列最顶部的空白格片段正是墙体的「空白」,而其余非顶部的空白格片段即墙体的「空洞」。

www.301.net 36

作者使用一组「列集合」来讲述墙体的边界并记下墙体的指雁为羹,它的模型如下:

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>

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

www.301.net 37

「延续空洞」与「非连续空洞」的狠抓进程如下:

www.301.net 38

实在「空驶列车」放大于墙体上,也会有「空洞」类似的遍布形态 ——
延续与非三番五次。
www.301.net 39

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

图像通道分别与联合

要么以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()

结果如图所示 ![www.301.net 40]

像素块分析

和图像的色彩处理有二种方式相同,图像的变形处理也有使用矩阵和像素块分析三种办法,drawBitmapMesh()与操纵像素点来改变色彩的规律类似,是把图像分成了一个个的小块,然后经过转移每三个图像块来修改总体图像。

public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight,float[] verts, int vertOffset,  int[] colors, int colorOffset, Paint paint);

www.301.net 41

伊始图像

www.301.net 42

反过来图像

要利用drawBitmapMesh()方法就需先将图纸分割为多少个图像块。在图像上横纵各画N条线,而那横纵各N条线就交织成了NxN个点,而各类点的坐标则以x1,y1,x2,y2,…,xn,yn的花样保留在verts数组中,也正是说verts数组的每两位用来保存3个交织点,第1个是横坐标,第3个是纵坐标。而整整drawBitmapMesh()方法改变图像的艺术,便是靠那个坐标值的变动来重新定义每一个图像块,从而达到图像效果处理的效益。使用这几个措施能够兑现无数图像特效如旗帜飘扬、水波纹等效果。

3.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;
}
}
}

几何变换

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

  • 一 、图像的缩放和旋转

dst = img.resize((128, 128))
dst = img.rotate(45) # 顺时针角度表示
  • ② 、转换图像

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)

部分开源图像处理库


  • ###### GPUImage for Android

GPUImage
是iOS下二个开源的基于GPU的图像处理库,提供各类种种的图像处理滤镜,并且帮助照相机和录像机的实时滤镜。GPUImage
for
Android是它在Android下的落到实处,同样也是开源的。在这之中提供了几十各类普遍的图形滤镜API,且其编写制定是根据GPU渲染,处理速度相应也正如快,是三个不易的图片实时处理框架。
GitHub地址:https://github.com/CyberAgent/android-gpuimage

  • ###### ImageFilterForAndroid

支撑一百多样图片处理效果

  • ###### OpenCV

OpenCV是1个依据BSD许可(开源)发行的跨平台总结机视觉库,能够运作在Linux、Windows、Android和Mac
OS操作系统上。

参考资料:《Android群英传》

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
会依据对应的扭转做相应的动作(动画)。

图像矩阵变换

  • 在篇章上半部分中重点采纳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()

如倘诺SportageGB图片,那么转换为array之后,就成为了多少个rowscolschannels的三维矩阵,由此,大家得以采取img[i,j,k]来拜会像素值

  • 例1:打开图片,并私下添加一些椒盐噪声

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()

结果如图:www.301.net 43

  • 例2:将lena图像二值化,像素值大于128的成为1,不然变为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()

结果如下图所示:www.301.net 44

要是要对多个像素点实行操作,能够利用数组切片格局访问。切片情势赶回的是以钦赐间隔下标访问
该数组的像素值。上面是关于灰度图像的一部分例证:

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 销毁游戏

直方图

6. 问题

在天涯论坛有二个关于「消灭星星」的话题:popstar关卡是哪些设计的?

其一话题在最终提议了1个标题 ——
「不能化解和最大得分不满足过关条件的矩阵」

www.301.net 45

「不大概清除的矩阵」其实便是最大得分为0的矩阵,本质上是「最大得分不满足过关条件的矩阵」。

最大得分不满足过关条件的矩阵
求「矩阵」的最大得分是1个「背包难点」,求解的算法简单:对现阶段矩阵用「递归」的方式把装有的消灭分支都进行二回,并取最高分值。但是javascript 的「递归」极易「栈溢出」导致算法无法推行。

实在在和讯的话题中提到二个缓解方案:

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

以此解决方案代价是昂贵的!小编提供有源码并不曾缓解这些难题,而是用四个比较取巧的艺术:跻身娱乐前检查是事为「不能够化解矩阵」,倘使是再度生成关卡矩阵

在意:我利用的取巧方案并没有缓解难点。

① 、画灰度图直方图

绘图都得以调用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()

获取如下图所示:www.301.net 46

7. 结语

上面是本文介绍的「消灭星星」的线上 DEMO 的二维码:

www.301.net 47

玩耍的源码托管在:

多谢耐心阅读完本作品的读者。本文仅代表小编的个人观点,如有不妥之处请不吝赐教。
假诺对「H5游戏开发」感兴趣,欢迎关切大家的专栏。

贰 、彩色图片直方图

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

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()

如图: www.301.net 48

参考资料

  • Knapsack problem
  • NP-completeness
  • popstar关卡是什么样规划的?
  • 费雪耶兹乱序算法
  • 兵连祸结均分算法

    1 赞 收藏
    评论

www.301.net 49

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注