基于Kmeans完成简单场景的证件照换色
均值算法是一种聚类算法,其目的是根据数据点之间的相似性将它们分组到不同的簇中。使用该算法的一个关键目标是找到可以有效区分数据的群组。
增加群组数量通常会导致以下结果之一:
- 过拟合:如果群组数量过多,算法可能会试图过度拟合数据,导致模型在新数据上表现不佳。在图像分割的情况下,可能导致平滑的区域被细分,导致边界失真。
- 计算成本增加:随着群组数量的增加,算法的计算成本也会增加。更多的群组意味着更多的迭代次数,这可能会对性能产生负面影响。
- 模糊性提高:群组数量增加可能会导致部分数据点无法清晰地归类到任何一个群组中,从而模糊了聚类结果。
对于不太复杂的,我试了下 5个差不多能满足需求了。勉强凑合。
摆脱PS,自己也能改证件照了。
贴出结果图:
#ifndef CHANGEBGCOLOR_H #define CHANGEBGCOLOR_H #include <QWidget> #include<QImage> #include<QDebug> #include<QMessageBox> #include<QFileDialog> #include<QColorDialog> #include"opencv2/opencv.hpp" using namespace cv; namespace Ui { class ChangeBGColor; } class ChangeBGColor : public QWidget { Q_OBJECT public: explicit ChangeBGColor(QWidget *parent = nullptr); ~ChangeBGColor(); public: cv::Mat kmBgReplace(const Mat& src, const cv::Vec3b& newColor); private slots: void onSelectImg(); void onSaveAs(); void onSelectColor(); void onModifyColor(); private: Ui::ChangeBGColor *ui; Mat src; Mat res; QColor newColor; Vec3b vec3; }; #endif // CHANGEBGCOLOR_H
#include "changebgcolor.h" #include "ui_changebgcolor.h" ChangeBGColor::ChangeBGColor(QWidget *parent) : QWidget(parent), ui(new Ui::ChangeBGColor) { ui->setupUi(this); resize(800,500); connect(ui->selectImg,&QPushButton::clicked,this,&ChangeBGColor::onSelectImg); connect(ui->btnModify,&QPushButton::clicked,this,&ChangeBGColor::onModifyColor); connect(ui->saveAs,&QPushButton::clicked,this,&ChangeBGColor::onSaveAs); connect(ui->btnColor,&QPushButton::clicked,this,&ChangeBGColor::onSelectColor); } ChangeBGColor::~ChangeBGColor() { delete ui; } cv::Mat ChangeBGColor::kmBgReplace(const Mat &src, const Vec3b &newColor) { if (src.empty()) { printf("could not load image...\n"); return cv::Mat(); } int width = src.cols; int height = src.rows; int sampleCount = width * height; int clusterCount = 5; cv::Mat labels; cv::Mat centers; // Convert RGB data to sample data cv::Mat sample_data = src.reshape(3, sampleCount); cv::Mat data; sample_data.convertTo(data, CV_32F); // Run K-Means cv::TermCriteria criteria = cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 10, 0.1); cv::kmeans(data, clusterCount, labels, criteria, clusterCount, cv::KMEANS_PP_CENTERS, centers); // Generate mask cv::Mat mask = cv::Mat::zeros(src.size(), CV_8UC1); int index = labels.at<int>(0, 0); labels = labels.reshape(1, height); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { int c = labels.at<int>(row, col); if (c == index) { mask.at<uchar>(row, col) = 255; } } } cv::Mat se = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1)); cv::dilate(mask, mask, se); // Generate Gaussian weight cv::GaussianBlur(mask, mask, cv::Size(5, 5), 0); // Background replacement based on Gaussian weight cv::Mat result = cv::Mat::zeros(src.size(), CV_8UC3); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { float w1 = mask.at<uchar>(row, col) / 255.0; cv::Vec3b bgr = src.at<cv::Vec3b>(row, col); for (int i = 0; i < 3; i++) { bgr[i] = w1 * newColor[i] + bgr[i] * (1.0 - w1); } result.at<cv::Vec3b>(row, col) = bgr; } } return result; } void ChangeBGColor::onSelectImg() { QString imagePath = QFileDialog::getOpenFileName(this, "选择图片", "", "Images (*.png *.jpg *.jpeg)"); src = cv::imread(imagePath.toStdString()); qDebug() << imagePath; if (!src.empty()) { cv::Mat rgbSrc; cv::cvtColor(src, rgbSrc, cv::COLOR_BGR2RGB); // 将图像从BGR格式转换为RGB格式 QImage qimg(rgbSrc.data, rgbSrc.cols, rgbSrc.rows, rgbSrc.step, QImage::Format_RGB888); QPixmap pixmap = QPixmap::fromImage(qimg); ui->labSource->setPixmap(pixmap); ui->labSource->setScaledContents(true); } else { QMessageBox::warning(this, tr("错误"), tr("无法加载图片")); } } void ChangeBGColor::onSaveAs() { QString filePath = QFileDialog::getSaveFileName(this, tr("Save Image"), "", tr("PNG (*.png);;JPEG (*.jpg)")); if (!filePath.isEmpty()) { const QPixmap *pixmapConst = ui->labRes->pixmap(); QPixmap pixmap = *pixmapConst; if (!pixmap.isNull()) { if (filePath.endsWith(".png", Qt::CaseInsensitive)) { pixmap.save(filePath, "PNG"); } else if (filePath.endsWith(".jpg", Qt::CaseInsensitive)) { pixmap.save(filePath, "JPEG"); } } else { QMessageBox::warning(this, tr("Error"), tr("No image on the label.")); } } } void ChangeBGColor::onSelectColor() { QColorDialog dialog(this); // 创建一个颜色选择对话框 QColor color = dialog.getColor(); // 打开颜色对话框并获取选择的颜色 if(color.isValid()) { // 检查颜色是否有效 // 将选择的颜色应用到按钮的背景色 QString qss="background-color:"+color.name(); newColor=color; ui->btnColor->setStyleSheet(qss); } } void ChangeBGColor::onModifyColor() { qDebug()<<newColor.name(); vec3 = cv::Vec3b(newColor.blue(), newColor.green(), newColor.red()); // 使用选择的颜色更新vec3的值 res = kmBgReplace(src, vec3); // 处理后的结果存储在res中 cv::Mat rgbRes; cv::cvtColor(res, rgbRes, cv::COLOR_BGR2RGB); // 将图像从BGR格式转换为RGB格式 QImage qimgRes(rgbRes.data, rgbRes.cols, rgbRes.rows, rgbRes.step, QImage::Format_RGB888); QPixmap pixmapRes = QPixmap::fromImage(qimgRes); ui->labRes->setPixmap(pixmapRes); ui->labRes->setScaledContents(true); }#opencv##QT##换背景色#