前言
库
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_()
代码比较多,不想写注释了,直接演示一下把
操作
蓝色
运行,就只有一个按钮,先要打开图片
先来简单的,比如打开这张图片(这张图片是网上随便找的)
打开后
就会弹出
布局确实不好看。。。。(0.0)
可以修改迭代次数,默认为10,比如设为1
而且要用开运算,所以模式还要设为2
因为底色是蓝色的,所以点击蓝色的按钮,然后弹出颜色选择框,不如选择白色的
结果
可以看到,效果还是很好的,主要是大佬的二值化范围设定的好,然后可以选择保存。
白色
来张白色背景的照片(网上搜的)
同理,运行到这一步
经过多次对参数的修改以及,范围的修改
最终得到的图片是这样的,可以看到有白色的,很由可能是我对白色的选取不到位。。。
范围不好说,然后接下来进行人工处理
当然,手抖,确实有问题,修的不是很好
总结
对白色底色的范围选取,并不是很完美,有待实验
底色可以任意选择,黄蓝绿等
看
有瑕疵。。