步骤说明
1. 打开图片
QString MainWindow::openimage(){
ui->img1->clear();
ui->details1->clear();
ui->details2->clear();
QString fileName = QFileDialog::getOpenFileName(this,tr("Open Image"),".",
tr("Image File(*.png *.jpg *.jpeg *.bmp)"));
if(fileName.isEmpty()){
return "";
}
filepath = fileName;
Mat Image = cv::imread(filepath.toStdString(),IMREAD_COLOR);
if(Image.channels()!=3){
ui->details1->setText("Please Select a Appropriate Picture!");
return "";
}
cv::cvtColor(Image,Image,CV_BGR2RGB);
cv::resize(Image,Image,Size(ui->img1->width(),ui->img1->height()));
QImage img = QImage((const unsigned char*)(Image.data),Image.cols,Image.rows,
QImage::Format_RGB888);
ui->img1->setPixmap(QPixmap::fromImage(img));
flag=1;
return fileName;
}
2. 变更底色
组装数据
Mat MainWindow::mat_to_samples(Mat& image){
int w = image.cols;
int h = image.rows;
int samplecount = w*h;
int dims = image.channels();
Mat points(samplecount, dims, CV_32F, Scalar(10));
int index = 0;
for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) {
index = row*w + col;
Vec3b bgr = image.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]);
}
}
return points;
}
KMeans聚类
// 运行KMeans
int numCluster = 4;
Mat labels;
Mat centers;
TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 1.0);
kmeans(points, numCluster, labels, criteria, 10, KMEANS_PP_CENTERS, centers);
背景去除+遮罩生成
// 去背景+遮罩生成
Mat mask=Mat::zeros(src.size(), CV_8UC1);
int index = src.rows*2 + 2;
int cindex = labels.at<int>(index, 0);
int height = src.rows;
int 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) { // 背景
mask.at<uchar>(row, col) = 0;
} else {
mask.at<uchar>(row, col) = 255;
}
}
}
形态学操作+高斯模糊
// 腐蚀 + 高斯模糊
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
morphologyEx(mask,mask,MORPH_DILATE,kernel,Point(-1,-1),1);
morphologyEx(mask,mask,MORPH_ERODE,kernel,Point(-1,-1),3);
GaussianBlur(mask, mask, Size(3, 3), 0, 0);
通道混合
// 通道混合
RNG rng(12345);
Vec3b color;
unsigned blue,green,red;
QPushButton *btn=qobject_cast<QPushButton*>(sender()); //得到当前操作的按钮
if(btn==ui->redButton)
{
blue = 0;
green = 0;
red = 255;
}
else if(btn==ui->blueButton)
{
blue = 255;
green = 0;
red = 0;
}else if(btn==ui->whiteButton){
blue = 255;
green = 255;
red = 255;
}else{
QColor color = QColorDialog::getColor(Qt::green);
if(color.isValid()){
blue = color.blue();
green = color.green();
red = color.red();
}else{
QMessageBox::information(NULL,"error message","set color error");
return;
}
}
color[0] = blue;
color[1] = green;
color[2] = red;
Mat result(src.size(), src.type());
double w = 0.0;
int b = 0, g = 0, r = 0;
int b1 = 0, g1 = 0, r1 = 0;
int b2 = 0, g2 = 0, r2 = 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 {
w = 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*w + b2*(1.0 - w);
g = g1*w + g2*(1.0 - w);
r = r1*w + r2*(1.0 - w);
result.at<Vec3b>(row, col)[0] = b;
result.at<Vec3b>(row, col)[1] = g;
result.at<Vec3b>(row, col)[2] = r;
}
}
}
dst = result.clone();
cv::cvtColor(result,result,CV_BGR2RGB);
cv::resize(result,result,Size(ui->img1->width(),ui->img1->height()));
QImage img = QImage((const unsigned char*)(result.data),result.cols,result.rows,QImage::Format_RGB888);
ui->img2->setPixmap(QPixmap::fromImage(img));
3.保存图片
void MainWindow::savefile(){
if(dst.empty()){
QMessageBox::information(NULL,"Error Message","No pictures to save!");
return;
}
QString savefilename = QFileDialog::getSaveFileName(this,tr("Save Image"),".",tr("*.jpg;;*.png;;*.jpeg;;*.bmp"));
if(savefilename.isEmpty()){
return ;
}else{
cv::imwrite(savefilename.toStdString(),dst); //保存图片
}
}
完整代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <opencv2/opencv.hpp>
#include <QtDebug>
#include <QImage>
#include <QMessageBox>
#include <iostream>
#include <string>
#include <QFileDialog>
#include <QScreen>
#include <QColorDialog>
using namespace cv;
using namespace std;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setFixedSize(618,525); // 固定窗口大小
this->setWindowTitle(QString::fromStdString( "证件照背景变更"));
ui->openButton->setGeometry(26,440,73,23);
ui->redButton->setGeometry(125,440,73,23);
ui->whiteButton->setGeometry(224,440,73,23);
ui->blueButton->setGeometry(323,440,73,23);
ui->customButton->setGeometry(422,440,73,23);
ui->saveButton->setGeometry(521,440,73,23);
QObject::connect(ui->openButton,SIGNAL(clicked()),this,SLOT(openimage()));
QObject::connect(ui->redButton,SIGNAL(clicked()),this,SLOT(identificationphotobgchange()));
QObject::connect(ui->blueButton,SIGNAL(clicked()),this,SLOT(identificationphotobgchange()));
QObject::connect(ui->whiteButton,SIGNAL(clicked()),this,SLOT(identificationphotobgchange()));
QObject::connect(ui->customButton,SIGNAL(clicked()),this,SLOT(identificationphotobgchange()));
QObject::connect(ui->saveButton,SIGNAL(clicked()),this,SLOT(savefile()));
//QObject::connect(ui->aboutSoftware,SIGNAL(triggered(bool)),this,SLOT(aboutSoftwareSlot()));
QObject::connect(ui->details,SIGNAL(triggered(bool)),this,SLOT(usedinstruction()));
QObject::connect(ui->aboutSoftware,SIGNAL(triggered(bool)),this,SLOT(softwareslot()));
}
MainWindow::~MainWindow()
{
delete ui;
}
QString MainWindow::filepath="";
QString MainWindow::openimage(){
ui->img1->clear();
ui->details1->clear();
ui->details2->clear();
QString fileName = QFileDialog::getOpenFileName(this,tr("Open Image"),".",tr("Image File(*.png *.jpg *.jpeg *.bmp)"));
if(fileName.isEmpty()){
return "";
}
filepath = fileName;
Mat Image = cv::imread(filepath.toStdString(),IMREAD_COLOR);
if(Image.channels()!=3){
ui->details1->setText("Please Select a Appropriate Picture!");
return "";
}
cv::cvtColor(Image,Image,CV_BGR2RGB);
cv::resize(Image,Image,Size(ui->img1->width(),ui->img1->height()));
QImage img = QImage((const unsigned char*)(Image.data),Image.cols,Image.rows,QImage::Format_RGB888);
ui->img1->setPixmap(QPixmap::fromImage(img));
flag=1;
return fileName;
}
void MainWindow::identificationphotobgchange(){
ui->img2->clear();
if(flag!=1){QMessageBox::information(NULL,"Error Message","Please Select a Image!");return;}
photochange();
}
void MainWindow::photochange() {
Mat src = cv::imread(filepath.toStdString(),IMREAD_COLOR);
if (src.empty()) {
printf("could not load image...\n");
return;
}
cv::resize(src,src,Size(1275,1787),0,0,INTER_LINEAR);
// 组装数据
Mat points = mat_to_samples(src);
// 运行KMeans
int numCluster = 4;
Mat labels;
Mat centers;
TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 1.0);
kmeans(points, numCluster, labels, criteria, 10, KMEANS_PP_CENTERS, centers);
// 去背景+遮罩生成
Mat mask=Mat::zeros(src.size(), CV_8UC1);
int index = src.rows*2 + 2;
int cindex = labels.at<int>(index, 0);
int height = src.rows;
int 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) { // 背景
mask.at<uchar>(row, col) = 0;
} else {
mask.at<uchar>(row, col) = 255;
}
}
}
// 腐蚀 + 高斯模糊
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
morphologyEx(mask,mask,MORPH_DILATE,kernel,Point(-1,-1),1);
morphologyEx(mask,mask,MORPH_ERODE,kernel,Point(-1,-1),3);
GaussianBlur(mask, mask, Size(3, 3), 0, 0);
// 通道混合
RNG rng(12345);
Vec3b color;
unsigned blue,green,red;
QPushButton *btn=qobject_cast<QPushButton*>(sender()); //得到当前操作的按钮
if(btn==ui->redButton)
{
blue = 0;
green = 0;
red = 255;
}
else if(btn==ui->blueButton)
{
blue = 255;
green = 0;
red = 0;
}else if(btn==ui->whiteButton){
blue = 255;
green = 255;
red = 255;
}else{
QColor color = QColorDialog::getColor(Qt::green);
if(color.isValid()){
blue = color.blue();
green = color.green();
red = color.red();
}else{
QMessageBox::information(NULL,"error message","set color error");
return;
}
}
color[0] = blue;
color[1] = green;
color[2] = red;
Mat result(src.size(), src.type());
double w = 0.0;
int b = 0, g = 0, r = 0;
int b1 = 0, g1 = 0, r1 = 0;
int b2 = 0, g2 = 0, r2 = 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 {
w = 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*w + b2*(1.0 - w);
g = g1*w + g2*(1.0 - w);
r = r1*w + r2*(1.0 - w);
result.at<Vec3b>(row, col)[0] = b;
result.at<Vec3b>(row, col)[1] = g;
result.at<Vec3b>(row, col)[2] = r;
}
}
}
dst = result.clone();
cv::cvtColor(result,result,CV_BGR2RGB);
cv::resize(result,result,Size(ui->img1->width(),ui->img1->height()));
QImage img = QImage((const unsigned char*)(result.data),result.cols,result.rows,QImage::Format_RGB888);
ui->img2->setPixmap(QPixmap::fromImage(img));
}
Mat MainWindow::mat_to_samples(Mat& image){
int w = image.cols;
int h = image.rows;
int samplecount = w*h;
int dims = image.channels();
Mat points(samplecount, dims, CV_32F, Scalar(10));
int index = 0;
for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) {
index = row*w + col;
Vec3b bgr = image.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]);
}
}
return points;
}
void MainWindow::savefile(){
if(dst.empty()){
QMessageBox::information(NULL,"Error Message","No pictures to save!");
return;
}
QString savefilename = QFileDialog::getSaveFileName(this,tr("Save Image"),".",tr("*.jpg;;*.png;;*.jpeg;;*.bmp"));
if(savefilename.isEmpty()){
return ;
}else{
cv::imwrite(savefilename.toStdString(),dst);
}
}
void MainWindow::aboutSoftwareSlot(){
aboutsoft *dialog = new aboutsoft;
dialog->show();
}
void MainWindow::usedinstruction(){
ui->img2->clear();
ui->img2->setStyleSheet("background-color: rgb(0, 100, 0);font-size:17px;color:white");
ui->img2->setText("使用说明:\r\n"
"1.文件仅支持英文路径\r\n"
"2.免费分享勿喷可建议\r\n"
"3.最好选用高清图片(>>1M)\r\n\r\n"
"欢迎探讨:\r\n"
"1.视觉算法\r\n"
"2.深度学习\r\n"
"3.哲学问题\r\n"
"4.伴侣关系\r\n"
"5.不婚主义者\r\n"
);
}
void MainWindow::softwareslot(){
ui->img1->clear();
ui->img1->setStyleSheet("background-color: rgb(0, 100, 0);font-size:18px;color:white");
ui->details2->setStyleSheet("font-size:18px;color:white");
ui->details1->setStyleSheet("font-size:18px;color:white");
ui->details2->setOpenExternalLinks(true);
ui->details1->setText("PureBgChange v3.0.3\r\n"
"版权归作者 灵魂玩家 所有\r\n"
"Email:1375593049@qq.com");
ui->details2->setText("CSDN:<style> a {text-decoration: none} </style>" "<a style='color: yellow;' href=\"https://blog.csdn.net/qq_36132590\">blog.soulplayer.net");
ui->details1->setAlignment(Qt::AlignLeft|Qt::AlignBottom);
ui->details2->setAlignment(Qt::AlignLeft|Qt::AlignTop);
}
结果展示
软件分享
百度云:04if
事情的胜败得失,我不是很在乎,我在乎的是我喜欢这份工作,乐于从事的这项事业。如果努力之后,我还是失败了,那我相信自己还会努力的