前言

opencv-python

pyqt5

换色原理

直言的说,在这篇大佬的文章中,

(32条消息) Python 教你用OpenCV实现给照片换底色_叶庭云的博客

可以实现换底色,我认为其中最关键的一步

binary_img = cv2.inRange(gray_img, low_value, high_value)
# 这个函数inRange

这个函数--相当于是二值化函数,在low_value与high_value之间的变成255,否则变成0

效果还是很好的,但是就只有蓝色换成其他颜色,比如,我想把白色底色的图片换成其他颜色怎么办??

pyqt5处理边缘

直言的说。我参考了这篇大佬的博客

(32条消息) Python12 PyQt5实现鼠标移动绘制图(自定义画笔粗细和颜色,橡皮擦功能,保存文件)_pyqt5 鼠标移动后固定颜色_今年的浙江省状元的博客

这篇博客,我觉得---最关键的一步

class PaintBoard(QWidget):
    ...

创建一个画板(新的窗口),与主窗口进行交互,主窗口控制颜色,大小之类的参数。传到画板中。

简单的说:不同窗口的交互。

这就很重要了。

正文

换色的页面

import cv2 as cv
import numpy as np
class change_color:
    def __init__(self,picture):
        """
        :param picture: 已经被imread读取的图片
        """
        self.iter = None
        self.picture=picture
        self.color=None
        self.num=None
    def choice_mode(self,num):
# 选择模式,对底色不同的图片,实际上模式是不一样的
        if num==1:
            return cv.MORPH_CLOSE
        else:
            return cv.MORPH_OPEN
    def set_num(self,num):
# 设置迭代次数
        self.num=num
    def get_color(self,color):
# 得到颜色
        self.color=color
    def white(self):
# 白色的范围
# 这个感觉要自己调节,自己尝试
        min_value = np.array([0,5, 5])
        max_value = np.array([255, 255, 255])
        return [min_value,max_value]
    def blue(self):
# 这个范围就用第一位大佬的博客的范围
        min_value = np.array([90, 70, 70])
        max_value = np.array([110, 255, 255])
        return [min_value,max_value]
    def start(self,color,what):
# what 确定原来的底色,这一步参考了大佬的博客
        rows, cols, channels = self.picture.shape
        gray_img = cv.cvtColor(self.picture, cv.COLOR_BGR2HSV)
        binary_img = cv.inRange(gray_img, color()[0], color()[1])
        if what=='白色':
# 如果底色是白色的,需要对二值化后的照片进行反色处理
            binary_img=255-binary_img
        kernel=cv.getStructuringElement(cv.MORPH_RECT,(5,5))
        mv1=cv.morphologyEx(binary_img,self.choice_mode(self.num),kernel,iterations=self.iter)
# 分别确定是开运算,还是闭运算,其实也可以对卷积核进行设置。
        for i in range(rows):
            for j in range(cols):
                if mv1[i, j] == 255:
                    self.picture[i, j] = self.color
# 对颜色的替换,参考了第一位大佬的博客,但是颜色可以任意,由pyqt5来选择
    def return_picture(self):
# 把处理后的图片进行其他处理,边缘处理
        self.picture=cv.cvtColor(self.picture,cv.COLOR_BGR2RGB)
        return self.picture
    def set_iter(self,iter):
# 设置迭代次数
        self.iter=iter
    def main(self,what):
# 对不同底色的图片进行选择
        match what:
            case '白色':
                self.start(self.white,what)
            case '蓝色':
                self.start(self.blue,what)

实际上底色还可以由红色,这里就没写出来,但应该差不多

对白色的底色变成其他颜色的底色,需要进行一步 反色操作,(自己尝试过就会知道为什么要反色)

😛😛😛

我用了最简单的方法

binary_img=255-binary_img

肯定有其他方法。可以去搜一搜,这里就不说明了。

pyqt5的页面

import os
import cv2 as cv
import numpy as np
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from color_base import change_color

class cv_Board(QWidget):
# 创建画板,传入参数
    def __init__(self,picture,spin,color=Qt.black):
        super().__init__()
        self.paint = None
        self.spin=spin
        self.color=color
        self.picture=picture
        self.initui()
    def initui(self):
        self.paint=QPainter(self)
    def paintEvent(self, e):
        if self.picture:
            self.paint.begin(self)
            self.paint.drawPixmap(0,0,self.picture)
            self.paint.end()
    def get_picture(self,picture):
        self.clear()
        self.picture=picture
    def clear(self):
        self.picture.fill(Qt.white)
    def mouseMoveEvent(self, e):
        if self.picture:
            self.paint.begin(self.picture)
            self.paint.setPen(QPen(self.color, self.spin))
            self.paint.drawPoint(e.pos())
            self.paint.end()
            self.update()
    def change_spin(self,spin):
        self.spin=spin
    def get_color(self,color):
        self.color=color
    def save(self):
        image = self.picture.toImage()
        return image

class Main(QWidget):
# 核心
    def __init__(self,picture):
        super().__init__()
        self.nums = None
        self.iters = None
        self.picture = None
        self.cv_img = None
        self.color = None
        self.color_change = None
        self.board = None
        self.size = None
        self.spin = None
        self.pen_window=None
        self.paint = None
        self.img = None
        self.filename = picture
        self.initui()
    def initui(self):
        self.setWindowTitle('核心')
        self.set_layout()
    def set_layout(self):
        h=QHBoxLayout(self)
        h1=QHBoxLayout(self)
        v2=QVBoxLayout(self)
        g=QGridLayout(self)
        p=self.get_widget()
        m=n=0
        for i in p:
            g.addWidget(i,m,n,alignment=Qt.AlignTop)
            if m>=n:
                n+=1
            else:
                m,n=n,m
        a=QSpacerItem(20,20)
        g.addLayout(v2,3,0)
        w1=self.get_widget2()
        for i in w1:
            v2.addWidget(i)
        h1.addWidget(self.get_paint())
        h.addLayout(h1)
        h.addLayout(g)
        return h
    def get_widget(self):
        label1=QLabel('笔的大小',self)
        self.spin=QSpinBox(self)
        self.spin.setValue(5)
        self.size=5
        self.spin.valueChanged.connect(self.spin_value)
        label2=QLabel('颜色',self)
        self.color_change=QPushButton('选择颜色',self)
        self.color_change.clicked.connect(self.color_value)
        save=QPushButton('保存',self)
        save.clicked.connect(self.save_picture)
        return label1,self.spin,label2,self.color_change,save
    def get_widget2(self):
        label=QLabel('迭代次数',self)
        self.iters=QSpinBox(self)
        self.iters.setValue(10)
        mode=QLabel('模式',self)
        self.nums=QSpinBox(self)
        self.nums.setValue(1)
        self.nums.setMinimum(1)
        self.nums.setMaximum(2)
        white=QPushButton('白色',self)
        white.clicked.connect(self.white_pic)
        blue=QPushButton('蓝色',self)
        blue.clicked.connect(self.white_pic)
        return label,self.iters,mode,self.nums,white,blue
    def get_iters(self):
        return self.iters.value()
    def get_nums(self):
        return self.nums.value()
    def imread(self,path):
# 解决中文路径问题
        cv_img = cv.imdecode(np.fromfile(path,dtype=np.uint8),-1)
        return cv_img
    def white_pic(self):
        text = self.sender().text()
        self.picture = self.imread(self.filename)
        tool = change_color(self.picture)
        tool.get_color(self.color_value())
        tool.set_iter(self.get_iters())
        tool.set_num(self.get_nums())
        tool.main(text)
        img = tool.return_picture()
        h, w, _ = img.shape
        img = QImage(img.data, w, h,w*3,QImage.Format_RGB888)
        img=QPixmap.fromImage(img)
        self.board.get_picture(img)
    def main(self,what):
# switch的选择,与可以用if...else..
        match what:
            case '白色':
                self.start(self.white)
            case '蓝色':
                self.start(self.blue)

    def save_picture(self):
        path,ok = QFileDialog.getSaveFileName(self, '保存图片', '.\\', '(*.jpg *.png)')
        if ok:
            img=self.board.save()
            img.save(path)

    def color_value(self):
        color=QColorDialog.getColor()
        self.board.get_color(color)
        color=color.getRgb()[:-1][::-1]
        return color
    def spin_value(self):
        self.size=self.spin.value()
        self.board.change_spin(self.size)
    def get_paint(self):
        image=QPixmap(self.filename)
        self.board=cv_Board(image,self.size)
        return self.board
class Start(QWidget):
# 开始页面
    def __init__(self):
        super().__init__()
        self.filename=None
        self.main=None
        self.initui()
    def initui(self):
        self.resize(640,480)
        self.setWindowTitle('主页面')
        self.set_layout()
    def set_layout(self):
        v1=QHBoxLayout(self)
        open_btn=self.get_btn()
        v1.addWidget(open_btn)
        return v1
    def get_btn(self):
        btn1=QPushButton('打开图片',self)
        btn1.clicked.connect(self.open)
        return btn1
    def open(self):
        filename, ok = QFileDialog.getOpenFileName(self, '打开文件', os.getcwd(), '(*.jpg *.png)')
        if ok:
            self.filename=filename
            self.main=Main(self.filename)
            self.main.setWindowModality(Qt.ApplicationModal)
            self.main.showMaximized()
    def paintEvent(self, a0):
        paint = QPainter(self)
        pixmap = QPixmap('C:/Users/520/PycharmProjects/pythonProject4/图片/图片/29.jpg')
        paint.drawPixmap(self.rect(), pixmap)



if __name__ == '__main__':
    app = QApplication(sys.argv)
    a = Start()
    a.show()
    app.exec_()

代码比较多,不想写注释了,直接演示一下把

操作

蓝色

运行,就只有一个按钮,先要打开图片




opencv java 去背景 opencv替换背景颜色python_python


先来简单的,比如打开这张图片(这张图片是网上随便找的)


opencv java 去背景 opencv替换背景颜色python_opencv java 去背景_02


打开后

就会弹出


opencv java 去背景 opencv替换背景颜色python_opencv_03


布局确实不好看。。。。(0.0)

可以修改迭代次数,默认为10,比如设为1

而且要用开运算,所以模式还要设为2

因为底色是蓝色的,所以点击蓝色的按钮,然后弹出颜色选择框,不如选择白色的

结果


opencv java 去背景 opencv替换背景颜色python_开发语言_04


可以看到,效果还是很好的,主要是大佬的二值化范围设定的好,然后可以选择保存。

白色

来张白色背景的照片(网上搜的)


opencv java 去背景 opencv替换背景颜色python_qt5_05


同理,运行到这一步


opencv java 去背景 opencv替换背景颜色python_opencv_06


经过多次对参数的修改以及,范围的修改

最终得到的图片是这样的,可以看到有白色的,很由可能是我对白色的选取不到位。。。

范围不好说,然后接下来进行人工处理


opencv java 去背景 opencv替换背景颜色python_开发语言_07


当然,手抖,确实有问题,修的不是很好

总结

对白色底色的范围选取,并不是很完美,有待实验

底色可以任意选择,黄蓝绿等


opencv java 去背景 opencv替换背景颜色python_qt5_08


有瑕疵。。