import cv2
from sklearn.datasets import load_iris
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import math
class PCAIris:
def __init__(self):
# 加载iris数据集, 不需要标签
self.data, _ = load_iris(return_X_y=True)
# 取两个特征,因为我们需要可视化到二位空间, 而且只绘制其中第二类50个样本点
self.data = self.data[50:100,[0,2]]
# 绘制的图像
self.img = np.ndarray(shape=(1024, 1024, 3), dtype=np.uint8)
self.img.fill(255) # 白色背景
def draw_pca_dir(self, point1, point2, color, scale=0.2):
"""
point1, point2是线段的两个点,
color是线段的颜色
scale是放大因子,根据样本的大小对应放大缩小
线段的箭头,长度为9
"""
# 绘制pca的两个主成分方向
# 计算pca线段的方向
angle = math.atan2(float(point1[1] - point2[1]), float(point1[0] - point2[0]))
# 计算两个点之间距离(线段长度)
distance = math.sqrt(float(point1[1] - point2[1]) ** 2 + float(point1[0] - point2[0]) ** 2)
# point1不动,按照scale进行线段放大
new_x = int(point1[0] - scale * distance * math.cos(angle))
new_y = int(point1[1] - scale * distance * math.sin(angle))
self.img = cv2.line(self.img, point1, (new_x, new_y), color, 1, cv2.LINE_AA)
# 绘制箭头(以point2为中心点)
len_arrow = 10 * 2
# 箭头的第01个线段
arrow1_x = int(new_x + len_arrow * math.cos(angle + math.pi / 4.0))
arrow1_y = int(new_y + len_arrow * math.sin(angle + math.pi / 4.0))
self.img = cv2.line(self.img, (arrow1_x, arrow1_y), (new_x, new_y), color, 1, cv2.LINE_AA)
# 箭头的第02个线段
arrow2_x = int(new_x + len_arrow * math.cos(angle - math.pi / 4.0))
arrow2_y = int(new_y + len_arrow * math.sin(angle - math.pi / 4.0))
self.img = cv2.line(self.img, (arrow2_x, arrow2_y), (new_x, new_y), color, 1, cv2.LINE_AA)
def draw_samples(self):
# 根据图像大小缩放样本
# 获取样本点最大与最小值,用来缩放
max_x, max_y = np.amax(self.data, axis=0)
min_x, min_y = np.amin(self.data, axis=0)
max_x += 0.5
max_y += 0.5
min_x -= 0.5
min_y -= 0.5
# 图像的大小
h, w, _ = self.img.shape # 深度不考虑
# 计算缩放比例
scale_x = w / (max_x - min_x)
scale_y = h / (max_y - min_y)
color = (0, 0, 255) # 绘制颜色
# 循环绘制样本点
for x, y in self.data:
# 平移到原点放大
x -= min_x
y -= min_y
x *= scale_x
y *= scale_y
self.img = cv2.circle(self.img, (int(x), int(y)), 2, color, -1)
def draw_pca(self):
# 计算样本的最大最小值
max_x, max_y = np.amax(self.data, axis=0)
min_x, min_y = np.amin(self.data, axis=0)
# 扩边,防止数据点落在边界
max_x += 0.5
max_y += 0.5
min_x -= 0.5
min_y -= 0.5
# 图像的大小
h, w, _ = self.img.shape # 深度不考虑
# 计算缩放比例
scale_x = w / (max_x - min_x)
scale_y = h / (max_y - min_y)
# 计算中心点
c_x, c_y = np.mean(self.data, axis=0)
# 中心点平移
c_x -= min_x
c_y -= min_y
# 放大
c_x *= scale_x
c_y *= scale_y
# 绘制中心点
cv2.circle(self.img, (int(c_x), int(c_y)), 5, (255, 0, 0), 2)
# 计算PCA特征值与特征向量
e_mean, e_vec, e_val = cv2.PCACompute2(self.data, np.mean(self.data, axis=0).reshape(1, -1))
# 使用特征值与特征向量构造两个坐标轴
p1_x = int(c_x) + int(e_vec[0][0] * e_val[0][0] * scale_x)
p1_y = int(c_y) + int(e_vec[0][1] * e_val[0][0] * scale_y)
p2_x = int(c_x) + int(e_vec[1][0] * e_val[1][0] * scale_x)
p2_y = int(c_y) + int(e_vec[1][1] * e_val[1][0] * scale_y)
self.draw_pca_dir((int(c_x), int(c_y)), (p1_x, p1_y), (255, 0, 0), 1.5)
self.draw_pca_dir((int(c_x), int(c_y)), (p2_x, p2_y), (255, 0, 0), 5)
class PCAApply:
def __init__(self, filename):
self.img = cv2.imread(filename)
# 把图像转换为灰度图,使用轮廓抽取,分成不同目标,每个目标判定方向
def getObjects(self):
# 灰度图像
img_gray = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
# 二值化图像(第一个参数返回优化的阈值)
_, img_bin = cv2.threshold(img_gray, 50,255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# 抽取轮廓(发现物体)
contours, _ = cv2.findContours(img_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
# 循环处理轮廓
for i in range(len(contours)):
# 获取轮廓的面积,面积过大过小都不绘制
area = cv2.contourArea(contours[i])
if area < 0.001 or area >20000:
continue
# 绘制轮廓
cv2.drawContours(self.img, contours, i, (0, 0, 255), 3)
# 计算每个目标的方向
self.getOrientation(contours[i])
def draw_pca_dir(self, point1, point2, color, scale=0.2):
"""
point1, point2是线段的两个点,
color是线段的颜色
scale是放大因子,根据样本的大小对应放大缩小
线段的箭头,长度为9
"""
# 绘制pca的两个主成分方向
# 计算pca线段的方向
angle = math.atan2(float(point1[1] - point2[1]), float(point1[0] - point2[0]))
# 计算两个点之间距离(线段长度)
distance = math.sqrt(float(point1[1] - point2[1]) ** 2 + float(point1[0] - point2[0]) ** 2)
# point1不动,按照scale进行线段放大
new_x = int(point1[0] - scale * distance * math.cos(angle))
new_y = int(point1[1] - scale * distance * math.sin(angle))
self.img = cv2.line(self.img, point1, (new_x, new_y), color, 1, cv2.LINE_AA)
# 绘制箭头(以point2为中心点)
len_arrow = 10
# 箭头的第01个线段
arrow1_x = int(new_x + len_arrow * math.cos(angle + math.pi / 4.0))
arrow1_y = int(new_y + len_arrow * math.sin(angle + math.pi / 4.0))
self.img = cv2.line(self.img, (arrow1_x, arrow1_y), (new_x, new_y), color, 1, cv2.LINE_AA)
# 箭头的第02个线段
arrow2_x = int(new_x + len_arrow * math.cos(angle - math.pi / 4.0))
arrow2_y = int(new_y + len_arrow * math.sin(angle - math.pi / 4.0))
self.img = cv2.line(self.img, (arrow2_x, arrow2_y), (new_x, new_y), color, 1, cv2.LINE_AA)
def getOrientation(self, contour):
# 先把contour处理成与iris一样的二维数据
data = np.squeeze(contour).astype(np.float) # 下面需要浮点数才能运算
# 计算均值中心点
c_x, c_y = np.mean(data, axis=0)
# print(data)
# 计算特征值与特征向量
_, e_vec, e_val = cv2.PCACompute2(data, np.mean(data, axis=0).reshape(1, -1), )
# 绘制中心点
self.img = cv2.circle(self.img, (int(c_x), int(c_y)), 3, (0, 0, 255), 2)
# 计算坐标轴的另外一个端点
p1_x = int(c_x + e_vec[0][0] * e_val[0][0] * 0.02) # 缩小0.02
p1_y = int(c_y + e_vec[0][1] * e_val[0][0] * 0.02)
p2_x = int(c_x + e_vec[1][0] * e_val[1][0] * 0.02)
p2_y = int(c_y + e_vec[1][1] * e_val[1][0] * 0.02)
# 绘制两�
评论0