kmeans简介
kmeans是机器学习中的一种聚类算法,简单来说就是把在一个区域中的点进行分类,分类的类别由自己定。具体的数学原理大家可以在很多的blog中学习,在这里主要介绍opencv中python的实现。在这里给大家一个非常直观的链接https://www.naftaliharris.com/blog/visualizing-k-means-clustering/大家可以访问该网址。里面由DBSCAN,kmeans等算法的可视化显示,下面给出一定的截图
通过这个可视化模型,大家可以非常好的学习kmeans和DBSCAN两种聚类算法
python代码实现
#__aditor:dwy
#__date: 2019/5/29
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import cv2 as cv
import numpy as np
img = cv.imread("tx.png")
#准备数据
color=[0,0,255]
wide = img.shape[1]
height = img.shape[0]
dims =img.shape[2]
sampleCount = wide*height
clusterCount = 4
points = np.zeros([sampleCount,dims],np.float32)#把准备好的数据存储到points中
centers = np.zeros([clusterCount,1],np.float32)
index = 0
for i in range(height):
for j in range(wide):
index = i*wide+j
for n in range(dims):
points[index][n]=img[i][j][n]
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 10, 0.1)
ret,label,center=cv.kmeans(points,clusterCount,None,criteria,10,cv.KMEANS_PP_CENTERS)#聚类的结果在ret中,标签为label ,中心为center
mask = np.zeros((img.shape[0],img.shape[1]),np.uint8)#创建一个掩模,如果是背景则为黑,不为背景则为255
index = wide*2+4#把第二行的第四个数据作为背景的标记(通常这个区域一般为背景,也可以设置某类包含点最多的为背景)
img2=img.copy()
cindex = label[index]#创建掩模的过程
for i in range(height):
for j in range(wide):
index = i * wide + j
label1 = label[index]
if cindex==label1:
for n in range(dims):
img2[i][j][n]=0;
mask[i][j]=0;
else:
mask[i][j]=255
structuting = cv.getStructuringElement(cv.MORPH_RECT,(11,11))#对图像进行开操作和膨胀处理
mask=cv.erode(mask,structuting)
mask=cv.GaussianBlur(mask,(9,9),0,0)#下面的循环是按照掩模复制图像,如果为0则设置成要替换的背景,如果不为0则把原图中的像素填充进去,clolr为预定的颜色
for i in range(height):
for j in range(wide):
if mask[i][j]==0:
for n in range(dims):
img2[i][j][n]=color[n]
else:
continue
cv.imshow("mask",img2)
cv.imshow("img",img)
cv.waitKey(0)
下面是代码运行的结果
左边是更换前的代码,右边是更换后的代码。
为了更好的使用该方法,本文提供qt可视化的更改代码
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'D:\My code\Python\PYQT\Background\background.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralWidget = QtWidgets.QWidget(MainWindow)
self.centralWidget.setObjectName("centralWidget")
self.gridLayout_4 = QtWidgets.QGridLayout(self.centralWidget)
self.gridLayout_4.setObjectName("gridLayout_4")
self.widget_2 = QtWidgets.QWidget(self.centralWidget)
self.widget_2.setObjectName("widget_2")
self.gridLayout_2 = QtWidgets.QGridLayout(self.widget_2)
self.gridLayout_2.setObjectName("gridLayout_2")
self.label_2 = QtWidgets.QLabel(self.widget_2)
self.label_2.setObjectName("label_2")
self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1)
self.label_4 = QtWidgets.QLabel(self.widget_2)
self.label_4.setObjectName("label_4")
self.gridLayout_2.addWidget(self.label_4, 0, 1, 1, 1)
self.before = QtWidgets.QLabel(self.widget_2)
self.before.setText("")
self.before.setObjectName("before")
self.gridLayout_2.addWidget(self.before, 1, 0, 1, 1)
self.after = QtWidgets.QLabel(self.widget_2)
self.after.setText("")
self.after.setObjectName("after")
self.gridLayout_2.addWidget(self.after, 1, 1, 1, 1)
self.gridLayout_2.setRowStretch(0, 1)
self.gridLayout_2.setRowStretch(1, 9)
self.gridLayout_4.addWidget(self.widget_2, 0, 0, 1, 1)
self.widget_3 = QtWidgets.QWidget(self.centralWidget)
self.widget_3.setObjectName("widget_3")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget_3)
self.horizontalLayout.setObjectName("horizontalLayout")
spacerItem = QtWidgets.QSpacerItem(148, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.widget = QtWidgets.QWidget(self.widget_3)
self.widget.setObjectName("widget")
self.gridLayout = QtWidgets.QGridLayout(self.widget)
self.gridLayout.setObjectName("gridLayout")
self.loadImage = QtWidgets.QPushButton(self.widget)
self.loadImage.setObjectName("loadImage")
self.gridLayout.addWidget(self.loadImage, 0, 0, 1, 1)
self.colorchoose = QtWidgets.QComboBox(self.widget)
self.colorchoose.setObjectName("colorchoose")
self.colorchoose.addItem("")
self.colorchoose.addItem("")
self.colorchoose.addItem("")
self.colorchoose.addItem("")
self.colorchoose.addItem("")
self.colorchoose.addItem("")
self.colorchoose.addItem("")
self.gridLayout.addWidget(self.colorchoose, 0, 1, 1, 1)
self.change = QtWidgets.QPushButton(self.widget)
self.change.setObjectName("change")
self.gridLayout.addWidget(self.change, 0, 2, 1, 1)
self.pushButton_2 = QtWidgets.QPushButton(self.widget)
self.pushButton_2.setObjectName("pushButton_2")
self.gridLayout.addWidget(self.pushButton_2, 1, 0, 1, 1)
self.pushButton_4 = QtWidgets.QPushButton(self.widget)
self.pushButton_4.setObjectName("pushButton_4")
self.gridLayout.addWidget(self.pushButton_4, 1, 1, 1, 1)
self.pushButton_5 = QtWidgets.QPushButton(self.widget)
self.pushButton_5.setObjectName("pushButton_5")
self.gridLayout.addWidget(self.pushButton_5, 1, 2, 1, 1)
self.horizontalLayout.addWidget(self.widget)
self.groupBox = QtWidgets.QGroupBox(self.widget_3)
self.groupBox.setObjectName("groupBox")
self.gridLayout_3 = QtWidgets.QGridLayout(self.groupBox)
self.gridLayout_3.setObjectName("gridLayout_3")
self.label_5 = QtWidgets.QLabel(self.groupBox)
self.label_5.setObjectName("label_5")
self.gridLayout_3.addWidget(self.label_5, 0, 0, 1, 1)
self.red = QtWidgets.QLineEdit(self.groupBox)
self.red.setText("")
self.red.setObjectName("red")
self.gridLayout_3.addWidget(self.red, 0, 1, 1, 1)
self.label_7 = QtWidgets.QLabel(self.groupBox)
self.label_7.setObjectName("label_7")
self.gridLayout_3.addWidget(self.label_7, 1, 0, 1, 1)
self.green = QtWidgets.QLineEdit(self.groupBox)
self.green.setText("")
self.green.setObjectName("green")
self.gridLayout_3.addWidget(self.green, 1, 1, 1, 1)
self.label_6 = QtWidgets.QLabel(self.groupBox)
self.label_6.setObjectName("label_6")
self.gridLayout_3.addWidget(self.label_6, 2, 0, 1, 1)
self.blue = QtWidgets.QLineEdit(self.groupBox)
self.blue.setText("")
self.blue.setObjectName("blue")
self.gridLayout_3.addWidget(self.blue, 2, 1, 1, 1)
self.gridLayout_3.setColumnStretch(0, 1)
self.gridLayout_3.setColumnStretch(1, 2)
self.horizontalLayout.addWidget(self.groupBox)
spacerItem1 = QtWidgets.QSpacerItem(148, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem1)
self.gridLayout_4.addWidget(self.widget_3, 1, 0, 1, 1)
self.textBrowser = QtWidgets.QTextBrowser(self.centralWidget)
font = QtGui.QFont()
font.setFamily("宋体")
font.setPointSize(12)
self.textBrowser.setFont(font)
self.textBrowser.setObjectName("textBrowser")
self.gridLayout_4.addWidget(self.textBrowser, 2, 0, 1, 1)
self.gridLayout_4.setRowStretch(0, 3)
self.gridLayout_4.setRowStretch(1, 1)
self.gridLayout_4.setRowStretch(2, 1)
MainWindow.setCentralWidget(self.centralWidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label_2.setText(_translate("MainWindow", "处理前图像"))
self.label_4.setText(_translate("MainWindow", "处理后图像"))
self.loadImage.setText(_translate("MainWindow", "导入图片"))
self.colorchoose.setItemText(0, _translate("MainWindow", "蓝色"))
self.colorchoose.setItemText(1, _translate("MainWindow", "白色"))
self.colorchoose.setItemText(2, _translate("MainWindow", "红色"))
self.colorchoose.setItemText(3, _translate("MainWindow", "灰色"))
self.colorchoose.setItemText(4, _translate("MainWindow", "紫色"))
self.colorchoose.setItemText(5, _translate("MainWindow", "黄色"))
self.colorchoose.setItemText(6, _translate("MainWindow", "绿色"))
self.change.setText(_translate("MainWindow", "确认更改"))
self.pushButton_2.setText(_translate("MainWindow", "待添加功能"))
self.pushButton_4.setText(_translate("MainWindow", "待添加功能"))
self.pushButton_5.setText(_translate("MainWindow", "待添加功能"))
self.groupBox.setTitle(_translate("MainWindow", "RGB通道"))
self.label_5.setText(_translate("MainWindow", " R"))
self.label_7.setText(_translate("MainWindow", " G"))
self.label_6.setText(_translate("MainWindow", " B"))
self.textBrowser.setHtml(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'宋体\'; font-size:12pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'SimSun\';\">以后还有什么图像处理相关的内容可以在此基础上扩展,RGB通道可以更改到任意背景颜色</span></p></body></html>"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
界面布局代码部分文件命名为Ui_background.py为后一个文件导入方便
# -*- coding: utf-8 -*-
"""
Module implementing MainWindow.
"""
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from Ui_background import Ui_MainWindow #如果此处的ui文件命名不同请改命名
import cv2 as cv
import numpy as np
import os
class MainWindow(QMainWindow, Ui_MainWindow):
"""
Class documentation goes here.
"""
def __init__(self, parent=None):
"""
Constructor
@param parent reference to the parent widget
@type QWidget
"""
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.color = [0, 0, 0]
self.target =True
self.i=0
self.newname =''
self.file_name=""
self.file_type =""
@pyqtSlot()
def on_loadImage_clicked(self):
"""
Slot documentation goes here.
"""
# TODO: not implemented yet
self.file_name, self.file_type = QFileDialog.getOpenFileName(self,"打开文件","D:/","All Files (*);")
##"open file Dialog "为文件对话框的标题,第三个是打开的默认路径,第四个是文件类型过滤器
img = cv.imread(self.file_name, -1)
if (img.shape[2] == 3):#bgr到rgb转换的显示
qimg = cv.cvtColor(img, cv.COLOR_BGR2RGB)
qimg = QImage(qimg.data, qimg.shape[1], qimg.shape[0], qimg.shape[1]*qimg.shape[2],QImage.Format_RGB888)
self.before.setPixmap(QPixmap.fromImage(qimg))
else:
qimg = QImage(img.data, img.shape[1], img.shape[0],QImage.Format_Grayscale8)
self.before.setPixmap(QPixmap.fromImage(qimg))
@pyqtSlot(str)
def on_colorchoose_currentIndexChanged(self, p0):
"""
Slot documentation goes here.
@param p0 DESCRIPTION
@type str
"""
# TODO: not implemented yet
current = self.colorchoose.currentText()
if self.target:
if current=="红色":
self.color =[0, 0, 255]
self.newname= '_red'
elif current=="白色":
self.color =[255, 255, 255]
self.newname= '_white'
elif current=="蓝色":
self.color =[255, 0, 0]
self.newname= '_blue'
elif current=="绿色":
self.color =[0, 255, 0]
self.newname= '_green'
elif current=="黄色":
self.color=[0, 255, 255]
self.newname= '_yellow'
elif current=="紫色":
self.color=[255, 0, 255]
self.newname= '_violet'
elif current=="灰色":
self.color=[96, 96, 96]
self.newname= '_gray'
@pyqtSlot()
def on_change_clicked(self):
"""
Slot documentation goes here.
"""
# TODO: not implemented yet
if self.red.text()!="":
self.red1 =int(self.red.text())
self.green1 =int(self.green.text())
self.blue1 =int(self.blue.text())
self.color = [self.blue1, self.green1,self.red1]
self.newname='_define'
print(self.color)
self.target = False
img =cv.imread(self.file_name, -1)
if (img is None):
print(1)
self.textBrowser.setText("打开图片失败,请重新加载")
else:
# 准备数据
wide = img.shape[1]
height = img.shape[0]
dims = img.shape[2]
sampleCount = wide * height
clusterCount = 4
points = np.zeros([sampleCount, dims], np.float32)
centers = np.zeros([clusterCount, 1], np.float32)
index = 0
for i in range(height):
for j in range(wide):
index = i * wide + j
for n in range(dims):
points[index][n] = img[i][j][n]
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 10, 0.1)
ret, label, center = cv.kmeans(points, clusterCount, None, criteria, 10, cv.KMEANS_PP_CENTERS)
mask = np.zeros((img.shape[0], img.shape[1]), np.uint8)
index = wide * 2 + 4
img2 = img.copy()
cindex = label[index]
for i in range(height):
for j in range(wide):
index = i * wide + j
label1 = label[index]
if cindex == label1:
for n in range(dims):
img2[i][j][n] = 0;
mask[i][j] = 0;
else:
mask[i][j] = 255
structuting = cv.getStructuringElement(cv.MORPH_RECT, (13, 13))
mask =cv.dilate(mask,structuting)
mask = cv.erode(mask, structuting)
structuting2 = cv.getStructuringElement(cv.MORPH_RECT, (9, 9))
mask = cv.erode(mask, structuting2)
cv.waitKey(10)
mask = cv.GaussianBlur(mask, (9, 9), 0, 0)
for i in range(height):
for j in range(wide):
if mask[i][j] == 0:
for n in range(dims):
img2[i][j][n] = self.color[n]
else:
continue
img2 = cv.GaussianBlur(img2, (3, 3), 0, 0)
if (img2.shape[2] == 3):
qimg = cv.cvtColor(img2, cv.COLOR_BGR2RGB)
qimg = QImage(qimg.data, qimg.shape[1], qimg.shape[0], qimg.shape[1] * qimg.shape[2],
QImage.Format_RGB888)
self.after.setPixmap(QPixmap.fromImage(qimg))
else:
qimg = QImage(img.data, img.shape[1], img.shape[0], QImage.Format_Grayscale8)
self.after.setPixmap(QPixmap.fromImage(qimg))
(fathername, filename) = os.path.split(self.file_name)#读取文件的父目录,文件名
(name, type) = os.path.splitext(filename)#分开文件名后缀名
name=fathername+"/"+name+self.newname+type
print(name)
cv.imwrite(name,img2)
write = "the image has been saved in:"+name
self.textBrowser.setText(write)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = MainWindow()
ui.show()
sys.exit(app.exec_())
实现部分
最后的实现效果如下图
只需要更改下拉框的参数便可以实现底色的改变。如果有需要的也可以用pyinstall 去打包py文件成可以执行的exe文件。
如果需要打包好的文件可以在下方留言,看到的话可以发给你
c++实现
int kmeans_picture()
{
Mat src = imread("tx.png");
Mat img2 = src.clone();
if (src.empty()) {
printf("could not load image...\n");
return -1;
}
namedWindow("input image", WINDOW_AUTOSIZE);
imshow("input image", src);
Scalar colorTab[] = {
Scalar(0, 0, 255),
Scalar(0, 255, 0),
Scalar(255, 0, 0),
Scalar(0, 255, 255),
Scalar(255, 0, 255)
};
int width = src.cols;
int height = src.rows;
int dims = src.channels();//获取图像的要素
// 初始化定义,准备数据阶段,把数据展开成width *height行的数据
int sampleCount = width*height;
int clusterCount = 4;//分类的组别
Mat points(sampleCount, dims, CV_32F, Scalar(10));//重新生成分类的数组
Mat labels;
Mat centers(clusterCount, 1, points.type());
// RGB 数据转换到样本数据
//int index = 0;
//for (int row = 0; row < height; row++) {
// for (int col = 0; col < width; col++) {
// index = row*width + col;
// Vec3b bgr = src.at<Vec3b>(row, col);
// points.at<float>(index, 0) = static_cast<int>(bgr[0]);
// points.at<float>(index, 1) = static_cast<int>(bgr[1]);
// points.at<float>(index, 2) = static_cast<int>(bgr[2]);
// }
//}
//指针索引方法
int index = 0;
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
index = row*width + col;
uchar *p = src.ptr<uchar>(row,col);
points.at<float>(index, 0) = static_cast<int>(p[0]);
points.at<float>(index, 1) = static_cast<int>(p[1]);
points.at<float>(index, 2) = static_cast<int>(p[2]);
}
}
// 运行K-Means
TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1);
kmeans(points, clusterCount, labels, criteria, 3, KMEANS_PP_CENTERS, centers);
Mat mask = Mat::zeros(src.size(), CV_8UC1);
index = src.rows * 2 + 2;
int cindex = labels.at<int>(index, 0);//背景设置为0
height = src.rows;
width = src.cols;
for (int row = 0;row<height;row++)
{
for (int col = 0;col<width;col++)
{
index = row*width + col;
int label = labels.at<int>(index, 0);
if (label == cindex)
{
img2.at<Vec3b>(row, col)[0] = 0;
img2.at<Vec3b>(row, col)[1] = 0;
img2.at<Vec3b>(row, col)[2] = 0;
mask.at<uchar>(row, col) = 0;
}
else
{
mask.at<uchar>(row, col) = 255;
}
}
}
//腐蚀
Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
erode(mask, mask, k);
imshow("win4", mask);
//高斯模糊
GaussianBlur(mask, mask, Size(3, 3), 0, 0);
imshow("win5", mask);
//通道混合
RNG rng(12345);
Vec3b color;
color[0] = rng.uniform(0, 255);
color[1] = rng.uniform(0, 255);
color[2] = rng.uniform(0, 255);
Mat result(src.size(), src.type());
double d1 = 0.0;
int r = 0, g = 0, b = 0;
int r1 = 0, g1 = 0, b1 = 0;
int r2 = 0, g2 = 0, b2 = 0;
for (int row = 0; row < height;row++)
{
for (int col = 0;col<width;col++)
{
int m = mask.at<uchar>(row, col);
if (m == 255)
{
result.at<Vec3b>(row, col) = src.at<Vec3b>(row, col);//前景
}
else if (m == 0)
{
result.at<Vec3b>(row, col) = color;//背景
}
else
{
d1 = m / 255.0;
b1 = src.at<Vec3b>(row, col)[0];
g1 = src.at<Vec3b>(row, col)[1];
r1 =src.at<Vec3b>(row, col)[2];
b2 = color[0];
g2 = color[1];
r2 = color[2];
b = b1*d1 + b2*(1.0 - d1);
g = g1*d1 + g2*(1.0 - d1);
r = r1*d1 + r2*(1.0 - d1);
result.at<Vec3b>(row, col)[0] = b;
result.at<Vec3b>(row, col)[1] = g;
result.at<Vec3b>(row, col)[2] = r;
}
}
}
//输出
imshow("win2", mask);
imshow("win3", img2);
imshow("win6", result);
//保存处理后的图片
waitKey(0);
imwrite("result-2.jpg", result);
}
效果和python的结果是一致的
小结
1本节主要用kmeans实现了照片底色的更改,主要涉及到了python的一些基础知识和opencv的使用
2.本节给出了python和c++两种方法的使用,也给出了基于界面的编写。大家可以参考一下如何在qt的lable控件中显示图片