python基于cv2轮廓识别切割雪碧图

背景:参与H5游戏前端开发工作一年多,工作之余也会学习node.js或者python来编写一些方便工作的脚本,工作的效率提高了不少,但是由于是项目中期加入工作室,故对于项目前期的各种知识都是属于欠缺的状态,故最近萌生出自己也写一款游戏的想法,以此来锻炼自己,提高自己的能力。

问题:开始准备阶段,在资源收集时,大多免费资源是一些打包的雪碧图,但是没有对应的plist/json配置文件。

常规办法:用ps抠选出对应的小图片保存。

思考:python有几个图像处理的库,应该有一些接口可以帮助我实现图集分割。

条件:雪碧图具有明显的透明区域界限(至少间隔一个透明像素)

处理方法一:

1、用PIL读取图片

2、numpy提取出透明通道

3、通过二维透明单通道矩阵提取出分割直线,从而得到对应的矩形

在进行矩阵处理的时候,由于本人对矩阵计算完全不熟,因此第3步执行不下去。

方法二:

1、2同上

3、将透明度矩阵二值化

4、通过cv2.findContours函数找到各个子图的轮廓

5、提取每个轮廓的最小外接矩形

6、切割保存

img = Image.open(fileName) #fileName 图片名字
img = img.convert('RGBA')  
np_data = np.array(img)    #转化成矩阵
np_a = np_data[:,:,3]      #提取出单透名通道
ret, binary = cv2.threshold(np_a,1,255,cv2.THRESH_BINARY)   #二值化处理,0跟透明度大于1
cnts,hierarchy=cv2.findContours(binary,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)   #提取轮廓
num = 1
    for cnt in cnts:  #遍历每个轮廓,提取出最小外接矩形
        minX = 0
        minY = 0
        maxX = 0
        maxY = 0
        for c in cnt:
            if c[0][0] > 0 :
                if minX == 0 or c[0][0] < minX:
                    minX = c[0][0]
            if c[0][1] > 0 :
                if minY == 0 or c[0][1] < minY:
                    minY = c[0][1]
            if c[0][0] > maxX:
                maxX = c[0][0]
            if c[0][1] > maxY:
                maxY = c[0][1]
        if maxX - minX  > 10 and maxY - minY > 10:   
#过滤到横竖像素小于10个的(按照实际需要调节)
            ig = img.crop((minX,minY,maxX,maxY))
            ig.save(newToPath + str(num) + ".png")  #newtoPath 自定义名称
            num += 1

通过这种方式就可以分离出界限明显的雪碧图。

其中也会有问题:比如爆炸特效,部分微小的单连通粒子会给分离切割出来,故加了最小像素限制。

python基于cv2轮廓识别切割雪碧图_第1张图片python基于cv2轮廓识别切割雪碧图_第2张图片

也存在雪碧图里的子图不是完全按矩形分布,切割之后可能会带着其他子图边缘。

另外,资源肯定不会只有一张,故需要批量进行处理。

这里我先创建了一个文件夹inPath = ('./texture/')

然后用os遍历目录,批量处理。

# encoding: utf-8

import cv2
import os
import numpy as np
from PIL import Image,ImageDraw

inPath = ('./texture/')  #输入输出目录
toPath = ('./')

def breakImage(fileName,newToPath):  #封装碎图方法
    img = Image.open(fileName)
    img = img.convert('RGBA')
    np_data = np.array(img)
    np_a = np_data[:,:,3]
    ret, binary = cv2.threshold(np_a,1,255,cv2.THRESH_BINARY)
    cnts,hierarchy=cv2.findContours(binary,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    num = 1
    for cnt in cnts:
        minX = 0
        minY = 0
        maxX = 0
        maxY = 0
        for c in cnt:
            if c[0][0] > 0 :
                if minX == 0 or c[0][0] < minX:
                    minX = c[0][0]
            if c[0][1] > 0 :
                if minY == 0 or c[0][1] < minY:
                    minY = c[0][1]
            if c[0][0] > maxX:
                maxX = c[0][0]
            if c[0][1] > maxY:
                maxY = c[0][1]
        if maxX - minX  > 10 and maxY - minY > 10:
            ig = img.crop((minX,minY,maxX,maxY))
            ig.save(newToPath + str(num) + ".png")  #将切割的子图片按照索引依次放到文件夹里
            num += 1

for root, dirs, files in os.walk(inPath):
    if len(root) > len(inPath): # 比较root和fromPath的字符长度 递归遍历文件夹
        innerPath= root[len(inPath):] 
        if innerPath[0] == '\\': # 判断innerPath的第一个字符是不是\符号
            innerPath = innerPath[1:] 
    for name in files:
        filePath = os.path.join(root, name)
        fileName, fileSuffix = os.path.splitext(name) # 分解文件名的扩展名
        if fileSuffix == '.png':
            newToPath = os.path.join(toPath,fileName,fileName) # 将toPath目录和fileName文件或文件夹拼接之后的路径赋值给newToPath
            isExists = os.path.exists(newToPath)   
#以文件名创建对应文件夹
            if not isExists:
                os.makedirs(newToPath) 
                print (newToPath+' 创建成功')
            breakImage(filePath,newToPath)

第一次发布帖子,如有纰漏或者错误,欢迎指出。

你可能感兴趣的:(python,开发语言,后端)