Python3 智能裁切图片
一、实验简介
1.1 知识点
• 使用 OpenCV 处理图片
• 边界检测
• 人脸识别
• 人眼识别
1.2 效果展示
二、实验步骤
本节将通过实践操作,带领大家使用Python3 实现智能裁切图片。
2.1 安装 OpenCV
由于安装过程需要等待很长时间,实验楼已经在环境中为大家安装OpenCV,可直接使用,以下安装步骤仅作参考
本课程的实验中使用了 OpenCV 3.1,因此我们需要编译安装它。
首先我们需要处理一个问题:当前实验楼的环境中 python3 命令使用的 python 版本为 3.5,但源中没有 python3.5-dev 的包,这会导致编译 OpenCV 出错,以及可能会出现其它问题。总而言之,我们需要将 python3 命令使用的 python 版本切换为 3.4。
$ sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.4 70 --slave /usr/bin/python3m python3m /usr/bin/python3.4m
然后安装一些依赖的包:
$ sudo apt-get update
$ sudo apt-get install python3-dev python3-numpy
$ sudo apt-get install cmake libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
现在可以开始编译 OpenCV 3.1 了,不过这个编译过程极其耗费时间(2个小时多),我相信各位同学们是没有耐心等待编译完成的,所以我在后面提供了编译后的二进制文件下载链接,大家直接使用就可以了。
下面是在实验楼环境中编译 OpenCV 3.1 所需的命令,其他环境中的编译请参考官网:http://docs.opencv.org/master/d7/d9f/tutorial_linux_install.html
$ wget https://github.com/Itseez/opencv/archive/3.1.0.zip
$ unzip 3.1.0.zip && cd opencv-3.1.0/
$ mkdir build && cd build
$ cmake -D CMAKE_BUILD_TYPE=Release \
-D CMAKE_INSTALL_PREFIX=/usr/local \
PYTHON3_EXECUTABLE=/usr/bin/python3 \
PYTHON_INCLUDE_DIR=/usr/include/python3.4 \
PYTHON_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython3.4m.so \
PYTHON3_NUMPY_INCLUDE_DIRS=/usr/local/lib/python3.4/dist-packages/numpy/core/include ..
$ make -j4
不想自己编译的同学请下载编译好的二进制文件,然后解压并进入 opencv-3.1.0/build 目录:
$ wget http://labfile.oss.aliyuncs.com/courses/637/opencv-3.1.0.tar.gz
$ tar xzvf opencv-3.1.0.tar.gz
$ cd opencv-3.1.0/build
然后我们开始安装:
$ sudo make install
2.2 程序原理
智能裁切图片的原理是尽可能保留图片的重要信息。
我们将图片划分为若干个区域,比如在本实验中,我们将图片分为很多个大小为 64x64 的块,假设一个图片有 n 个这样的块。有的块可能包含的信息更重要,有的则包含很少的重要信息,我们保留相对图片里相对有更重要信息的块。裁切后的图片尽可能多的包含这样的块,这即是智能裁切。
什么是重要信息呢,我们从这三个方面判断:
• 各个颜色通道的亮度
• 边界
• 有人脸,人眼
除了颜色,人的眼脸不必多说,至于边界,我们可以理解为纹理,纹理多的地方信息越丰富越可能重要不是么?
2.3 程序实现
先导入相关模块:
import sys
import copy
import cv2
import getopt
import math
边界检测我们使用 Canny 算法来检测,当然,算法不用我们实现,OpenCV 有现成的函数可以调用:
cv2.Canny()
关于 Canny 算法的信息,可以查看 Wikipedia 的介绍:https://zh.wikipedia.org/wiki/Canny算子
# 使用 Canny 算法检测边界
def detectEdges(image):
edges = cv2.Canny(image, 100, 100)
# 保存图片
cv2.imwrite("edges.jpg", edges)
return edges
把 edges 写入到图片类似这样的效果:
isolateUnique() 函数接受两个参数,一个是 OpenCV 打开的图片对象,一个是包含这个图片边界信息的图片对象。
isolateUnique() 函数件图片分为若干个 64x64 的块,将其中颜色信息高出平均值指定阈值、且包含边界信息的块设为白色的块,其余块都设为黑色。类似下面这样:
def isolateUnique(image, edges):
# 纵向分割为 64 个块
blocksize = 64
# 方差阈值
varianceThreshold = 95
# 设定每一块的边长,shape[1] 为图片宽度
cellPx = image.shape[1] // blocksize
rows = image.shape[0] // cellPx
cols = blocksize
# 每一块的像素数量
blockPx = cellPx * cellPx
cellValues = [0] * (rows)
imgR = 0
imgG = 0
imgB = 0
imgCells = (blockPx) * (rows * cols)
for i in range(rows):
cellValues[i] = [0] * blocksize
for j in range(cols):
rbeg = cellPx * i
rend = rbeg + cellPx
cbeg = cellPx * j
cend = cbeg + cellPx
r = 0
g = 0
b = 0
hasEdge = False
for ii in range(rbeg, rend):
for jj in range(cbeg, cend):
if edges[ii][jj] > 0:
hasEdge = True
r = r + image[ii][jj][0]
g = g + image[ii][jj][1]
b = b + image[ii][jj][2]
imgR = imgR + r
imgG = imgG + g
imgB = imgB + b
rv = int(r / blockPx)
gv = int(g / blockPx)
bv = int(b / blockPx)
value = [rv, gv, bv] if hasEdge == True else [0, 0, 0]
cellValues[i][j] = value
# 绘制实心矩形
cv2.rectangle(image, (cbeg, rbeg), (cend, rend), (rv, gv, bv), -1)
avgR = imgR // imgCells
avgG = imgG // imgCells
avgB = imgB // imgCells
# cv2.rectangle(image, (100,100), (200,200), (avgR,avgG,avgB), -1)
cv2.rectangle(image, (0, 0), (image.shape[1], image.shape[0]), (0, 0, 0),
-1)
for i in range(len(cellValues)):
for j in range(len(cellValues[i])):
rdiff = abs(cellValues[i][j][0] - avgR)
gdiff = abs(cellValues[i][j][1] - avgG)
bdiff = abs(cellValues[i][j][2] - avgB)
rbeg = cellPx * i
rend = rbeg + cellPx
cbeg = cellPx * j
cend = cbeg + cellPx
pxDiff = True if (rdiff > varianceThreshold or
gdiff > varianceThreshold or
bdiff > varianceThreshold) else False
isBlack = True if (cellValues[i][j][0] < 30 and
cellValues[i][j][1] < 30 and
cellValues[i][j][2] < 30) else False
if pxDiff and (isBlack == False):
cv2.rectangle(image, (cbeg, rbeg), (cend, rend),
(255, 255, 255), -1)
else:
cv2.rectangle(image, (cbeg, rbeg), (cend, rend), (0, 0, 0), -1)
cv2.imwrite("blocks.png", image)
然后是 `main(