做SAR图像分类预处理时用到K-means聚类把前景后景分开。由于聚类效果不太理想,总有些错分的像素点和目标区域不闭合的情况,且目标区域的边缘也不够平滑,所以在K-means之后还进行了一些后续操作,包括二值化处理,开运算等操作,再进行目标区域定位画框,效果就好很多。
完整代码和注释如下:
# -*- coding: utf-8 -*-
# autho:xier
import cv2
import os
import math
import numpy as np
import PIL.Image as image
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from pylab import mpl
mpl.rcParams['font.sans-serif'] = 'NSimSun, Times New Roman'
# 加载图片,转成矩阵
def load_data(file_path):
f = open(file_path, 'rb') # 二进制打开
data = []
img = image.open(f) # 以列表形式返回图片像素值
m, n = img.size # 图片大小
for i in range(m):
for j in range(n): # 将每个像素点RGB颜色处理到0-1范围内并存放data
x = img.getpixel((i, j))
data.append([x/256.0])
f.close()
return np.mat(data), m, n # 以矩阵型式返回data,图片大小
def K_means(img_data, row, col):
label = KMeans(n_clusters=2, n_init=40, init='k-means++').fit_predict(img_data) # 聚成两类
label = label.reshape([row, col]) # 聚类获得每个像素所属的类别
if np.sum(label) >= row * col/2: #分类相反
label = np.abs(np.ones((row, col)) - label)
pic_k = image.new("L", (row, col)) # 创建一张新的灰度图保存聚类后的结果
for i in range(row): # 根据所属类别向图片中添加灰度值
for j in range(col):
pic_k.putpixel((i, j), int(256 / (label[i][j] + 1)))
return pic_k
# 对K_means聚类分割后的图像进行阈值分割转成二值图像
def Thresh_and_blur(img):
(_, thresh) = cv2.threshold(img, 130, 255, cv2.THRESH_BINARY)
return thresh
# 用数字形态学先腐蚀掉噪声再进行膨胀(开运算)
def image_morphology(thresh):
# 建立一个椭圆核函数
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
# 执行图像形态学
opened = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
# closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, kernel)
#opened = cv2.erode(opened, None, iterations=4) # 腐蚀4次
#opened = cv2.dilate(opened, None, iterations=4) # 膨胀4次
return opened
def findcnts_and_box_point(opened):
# 这里opencv3返回的是三个参数,源图像、轮廓信息、可选参数
# 轮廓检索模式:cv2.RETR_LIST表示提取所有轮廓并记录在列表
# 轮廓逼近方法:cv2.CHAIN_APPROX_SIMPLE,压缩水平、垂直、对角元素,保留终点坐标,如矩形轮廓用4个角点表示
(_, cnts, _) = cv2.findContours(opened.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
c = sorted(cnts, key=cv2.contourArea, reverse=True)
c = c[1]
# compute the rotated bounding box of the largest contour
rect = cv2.minAreaRect(c) # 生成最小外接矩形,retval包含中心坐标、矩形宽高和旋转角度
box = np.int0(cv2.boxPoints(rect)) # 得到旋转矩阵四个顶点坐标
return box
def drawcnts_and_cut(original_img, box):
# draw a bounding box arounded the detected barcode and display the image
draw_img = cv2.drawContours(original_img.copy(), [box], -1, (0, 0, 255), 3)
# 截取图像
Xs = [i[0] for i in box]
Ys = [i[1] for i in box]
x1 = min(Xs)
x2 = max(Xs)
y1 = min(Ys)
y2 = max(Ys)
y_mid = y1 + math.ceil((y2 - y1)/2)
x_mid = x1 + math.ceil((x2 - x1) / 2)
crop_img = original_img[y_mid-25:y_mid+25, x_mid-25:x_mid+25] # 裁成50*50目标图像
return draw_img, crop_img
def walk():
original_Folder = '自己存放图片的文件夹'
OR_Vector = os.listdir(original_Folder) # 读取待处理文件夹中所有的文件
for k in range(len(OR_Vector)):
# 取一个类别文件夹
Class_Folder = os.path.join('%s\%s' % (original_Folder, OR_Vector[k]))
# 读取该类别文件夹的所有图片
CF_Tensor = os.listdir(Class_Folder)
for p in range(len(CF_Tensor)): # 取一张图片
# 取一张图片
Image_Path = os.path.join('%s\%s' % (Class_Folder, CF_Tensor[p]))
original_img = cv2.imread(Image_Path)
img_data, row, col = load_data(Image_Path)
pic_k = K_means(img_data, row, col)
pic_k = np.asarray(pic_k)
thresh = Thresh_and_blur(pic_k)
opened = image_morphology(thresh)
box = findcnts_and_box_point(opened)
draw_img, crop_img = drawcnts_and_cut(original_img, box)
# 保存切割后的图片
# Save_Folder = 'F:\pycharm\\test_IO\\MSTAR-10_denoise_enl4\\train_denoise_enl4_segment'
# SA_Vector = os.listdir(Save_Folder) # 读取保存的文件夹中所有的文件
# Class_Folder2 = os.path.join('%s\%s' % (Save_Folder, SA_Vector[k]))
# Save_Path = os.path.join('%s\%s' % (Class_Folder2, CF_Tensor[p]))
# cv2.imwrite(Save_Path, crop_img)
titles = [u'去噪后图像', u'K-means聚类后', u'二值化处理', u'开运算', u'定位目标区域', u'切割后的图像']
images = [original_img, pic_k, thresh, opened, draw_img, crop_img] #
for i in range(6):
plt.subplot(2, 3, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
cv2.waitKey(20180407)
walk()
结果展示如下:
