如果你手中有类似PS这样的软件,完成这个任务应该并不困难,不就是抠图么!!!但是,抠图需要我们自己手动找分割线啊,多麻烦呢,能不能让计算机自动完成这个工作呢?当然是可以的,利用上面说的SVM就可以办到。那么该怎么做呢?我们知道,彩色图片本质上是由一个一个的像素点组成的,每一个像素点由RGB三色组成,或者说本质上彩色图像就是三维数组,而灰度图像则是二维数组。如果我们将湖水和鸭子看做两类物体,那么现在的任务则是从整个图像中将这两类分割出来。显然鸭子与湖水的界限并不是一条单纯的直线,甚至有些地方是交杂在一起的,所以本质上这是一个非线性可分的问题。从图中可以看出,鸭子的颜色偏黑色和灰色,掺杂有少量白色以及黄色(鸭脚),而湖水则是浅绿色的。所以我们可以以颜色为标准对二者进行分类,即以RGB为分类标准。为了使用SVM,首先我们需要选取训练样本,这里就是找出典型的属于鸭子的像素点RGB值(为一个长度为3的向量),和属于湖水的RGB值。关于如何确定图像上某一点的RGB值,有很多办法,这里我推荐使用一个名为Colorpix的小软件,这个软件只有几百kb,一个exe执行文件,可以找出屏幕上任何一点的像素属性,用起来很方便,如果要用,请大家自行搜索。这里我对于湖水和鸭子分别选取了10个像素点,这样我就得到了一个20行3列的样本数据(每一行是一个样本,共有20个样本)。将湖水的像素点标记为0,鸭子的像素点标记为1,这样我们就可以得到长度为20的、前10个元素为0,后10个元素为1的向量。由于图像原始数据为三维矩阵,比如设其维度为
(m,n,k)
(m,n,k)
,我们首先需要将其转化为2维,即转化为
(mn,k)
(mn,k)
的矩阵,然后使用线性不可分的SVM训练样本数据,接着使用训练好的SVM对
(mn,k)
(mn,k)
矩阵进行归类,我们得到一个长为
mn
mn
的数据取0或者1的一维数组
predict
predict
,为0的部分就是代表对应的像素点判定为湖水了。接着将
predict
predict
数组在行的方向上扩展为3列,即变为
(predict,predict,predict)
(predict,predict,predict)
,扩展之后的矩阵维度为
(mn,k)
(mn,k)
,再将其变回三维矩阵,即
(m,n,k)
(m,n,k)
的矩阵。该矩阵与原始图像三维矩阵对应,该矩阵数据点为
(0,0,0)
(0,0,0)
的部分即判定为湖水,我们将图像上该像素点的RGB值变为
(255,255,255)
(255,255,255)
(白色),于是我们就可以得到去掉湖水(变为白色背景)的鸭子了。
1. Matlab 代码
% 使用SVM将鸭子从湖面分割
% 导入图像文件引导对话框
[filename,pathname,flag] = uigetfile('*.jpg','请导入图像文件');
Duck = imread([pathname,filename]);
%使用ColorPix软件从图上选取几个湖面的代表性点的RGB的值
LakeTrainData = [147,168,125;151 173 124;143 159 112;150 168 126;...
146 165 120;145 161 116;150 171 130;146 112 137;149 169 120;144 160 111];
% 从图中选取几个有代表性的鸭子点的RGB值
DuckTrainData = [81 76 82;212 202 193;177 159 157;129 112 105;167 147 136;...
237 207 145;226 207 192;95 81 68;198 216 218;197 180 128];
% 属于湖的点为0,鸭子的点为1
group = [zeros(size(LakeTrainData,1),1);ones(size(DuckTrainData,1),1)];
% 训练得到支持向量分类机
LakeDuckSVM = svmtrain([LakeTrainData;DuckTrainData],group,'kernel_function','polynomial',...
'polyorder',2);
[m,n,k] = size(Duck); % 图像三维矩阵
% 将Duck转化为双精度的m*n行,3列的矩阵
Duck1 = double(reshape(Duck,m*n,k));
% 根据训练得到的支持向量机对整个图像像素点进行分类
IndDuck = svmclassify(LakeDuckSVM,Duck1);
% 属于湖的点的逻辑数组
IndLake = ~IndDuck;
result = reshape([IndLake,IndLake,IndLake],[m,n,k]); % 与图片的维数对应
Duck2 = Duck;
Duck2(result)= 255; % 湖面的点变为白色
figure;
imshow(Duck2); % 显示分割之后的图像
2. Python的实现
from PIL import Image
import numpy as np
from sklearn.svm import SVC # 非线性 分类 SVM
pic = 'duck.jpg' # 鸭子图片
img = Image.open(pic)
img.show() # 显示原始图像
img_arr = np.asarray(img,np.float64)
# 选取湖面上的关键点RGB值(10个)
lake_RGB = np.array(
[[147,168,125],[151,173,124],[143,159,112],[150,168,126],[146,165,120],
[145,161,116],[150,171,130],[146,112,137],[149,169,120],[144,160,111]]
)
# 选取鸭子上的关键点RGB值(10个)
duck_RGB = np.array(
[[81,76,82],[212,202,193],[177,159,157],[129,112,105],[167,147,136],
[237,207,145],[226,207,192],[95,81,68],[198,216,218],[197,180,128]]
)
RGB_arr = np.concatenate((lake_RGB,duck_RGB),axis=0) # 按列拼接
# lake 用 0标记,duck用1标记
label = np.append(np.zeros(lake_RGB.shape[0]),np.ones(duck_RGB.shape[0]))
# 原本 img_arr 形状为(m,n,k),现在转化为(m*n,k)
img_reshape = img_arr.reshape([img_arr.shape[0]*img_arr.shape[1],img_arr.shape[2]])
svc = SVC(kernel='poly',degree=3) # 使用多项式核,次数为3
svc.fit(RGB_arr,label) # SVM 训练样本
predict = svc.predict(img_reshape) # 预测测试点
lake_bool = predict == 0. # 为湖面的序号(bool)
lake_bool = lake_bool[:,np.newaxis] # 增加一列(一维变二维)
lake_bool_3col = np.concatenate((lake_bool,lake_bool,lake_bool),axis=1) # 变为三列
lake_bool_3d = lake_bool_3col.reshape((img_arr.shape[0],img_arr.shape[1],img_arr.shape[2])) # 变回三维数组(逻辑数组)
img_arr[lake_bool_3d] = 255. # 将湖面像素点变为白色
img_split = Image.fromarray(img_arr.astype('uint8')) # 数组转image
img_split.show() # 显示分割之后的图像
img_split.save('split_duck.jpg') # 保存
参考:https://www.cnblogs.com/lyrichu/p/7221571.html