做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()
結果展示如下: