python使用opencv换照片底色的实现

 

python使用opencv换照片底色

第一次使用opencv,遇到了很多问题,记录一下

安装问题

代理

由于pip使用了代理而电脑代理没开,导致pip install opencv-python时一直报错连接不上代理

解决办法:

  • 1.使用pip install -i <清华源>
  • 2.开启代理

无法引入jar包

安装完成后在交互模式可以正常使用

输入python

import cv2

不报错就说明正常安装了

但是在jupyter notebook 中引入一直报错,找不到cv2模块

经过百度后,测试如下

交互模式下输入如下命令查看python环境

import sys
sys.executable

这是anaconda下的一个虚拟环境,没有问题

再在jupyter notebook下输入同样的命令,查看python环境,发现竟然不是上述环境

而是 anaconda3/share下的环境

至此,就查询到了问题的原因:

jupyter notebook 是anaconda的公共包

进入虚拟环境后 pip install jupyter 再次启动jupyter notebooke 就正常了

使用opencv及原理

使用opencv换照片底色的原理很简单

  • 读取照片
  • 将照片转换为灰度图
  • 提取灰度图底色的BGR上下边界
  • 使用opencv转换背景底色的BGR值(第三步已经获取了背景色的上下边界,边界之中的所有颜色都会被转换为255,边界之外的颜色都会被处理为0)
  • 循环处理像素点,将第四步转换后的255(背景色)转换为你想要的颜色
  • 输出,保存
import cv2
import numpy as np
# 读取照片
img=cv2.imread('zhuominghua.jpg')

# 图像缩放
img = cv2.resize(img,None,fx=0.5,fy=0.5)
rows,cols,channels = img.shape
print(rows,cols,channels)
cv2.imshow('[img]',img)

# 图片转换为灰度图
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
# 查看灰度图
cv2.imshow('hsv',hsv)

# 图片的二值化处理
lower_blue=np.array([100,0,200])
upper_blue=np.array([200,255,255])
mask = cv2.inRange(hsv, lower_blue, upper_blue)
print(mask)


#腐蚀膨胀
erode=cv2.erode(mask,None,iterations=1)
# cv2.imshow('erode',erode)

dilate=cv2.dilate(erode,None,iterations=1)
# cv2.imshow('dilate',dilate)

#遍历每个像素点,进行颜色的替换
for i in range(rows):
for j in range(cols):
  if erode[i,j]==255: # 像素点为255表示的是白色,我们就是要将白色处的像素点,替换为红色
    img[i,j]=(255,255,255) # 此处替换颜色,为BGR通道,不是RGB通道
# 显示处理后的图片
cv2.imshow('res',img)

# 保存
cv2.imwrite("zhuominghu_white.jpg", img)
# 窗口等待的命令,0表示无限等待
cv2.waitKey(0)

代码是参照网上的例子,经过实验,很好用

但是在图片的二值化处理阶段,比较麻烦,需要从灰度图中获取背景色的BGR值,再计算上下边界

灰度图的颜色使用BGR表示的,而不是 RGB

使用的到方法:

  • imread() 读取源图片
  • imshow() 展示图片
  • imwrite() 输出图片
  • cvtColor() 转换灰度图
  • inRange() 二值化处理,将上下边界内的颜色值处理为255,其他的颜色值处理为0

 

python+openCV一键换底色,不同底色自动判断

图来源于网络,未有冒犯之意

思路来源

最近到处需要用到一寸照,但是有些底色不同,一开始网上随便找了几个,但是完成后都是要收费的,后面用到removebg,抠图一键换底色,但是有像素限制,高像素需要收费下载,所以自己无聊用参考网上资料opencv写了个,网上都是单个颜色处理,并且对于参数有些有限制,在细节处理上不通用,所以自己重新写了一个并简单做了个界面,虽然比removebg差多了,不过好歹能用。

需求

  • 懒人式换一寸照底色
  • 界面应用
  • 多底色选择
  • 自动识别底色
  • 学习代码使用,要求不高

预览

在这里插入图片描述

对比

原图

图像来源于网络

红底

在这里插入图片描述

绿底

在这里插入图片描述

白底

在这里插入图片描述

代码

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'untitled.ui'
#
# Created by: PyQt5 UI code generator 5.13.0
#
# WARNING! All changes made in this file will be lost!

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox,QInputDialog,QFileDialog
import cv2,time,sys
import numpy as np

def change_bg_color(path,color):
  global new_path
  color_dict={'red':[0,0,255],'green':[0,255,0],'blue':[255,0,0],'white':[255,255,255]}
  color_list=color_dict[color]
  #导入图片,不能有中文路径
  # img=cv2.imread(path)
  #导入图片,可以有中文路径
  img = cv2.imdecode(np.fromfile(path, dtype=np.uint8), 1)
  #图片缩放
  #img=cv2.resize(img,None,fx=0.5,fy=0.5)
  #转换hsv,提取颜色
  #cv2.cvtColor是颜色空间转换函数,img是需要转换的图片,第二个是转换成何种格式。
  hsv=cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
  #判断当前图片底色
  if img[0][0][0]>200 and img[0][0][1]<230 and img[0][0][2]<230:
#         print('原图蓝色底')
      #提取颜色区域,不在范围的设为0,在范围的设为255
      hsv_min=np.array([47,79,79])
      hsv_max=np.array([102,255,255])
  elif img[0][0][1]>200 and img[0][0][0]<230 and img[0][0][2]<230:
#         print('原图绿色底')
      hsv_min = np.array([41,40,41])
      hsv_max = np.array([90,255,255])   
  elif img[0][0][2]>200 and img[0][0][0]<230 and img[0][0][1]<230:
#         print('原图红色底')
      hsv_min = np.array([0,200,40])
      hsv_max = np.array([10,255,255])
  else:
#         print('原图白色底')
      hsv_min = np.array([0,0,221])
      hsv_max = np.array([180,30,255])
      
  mask = cv2.inRange(hsv, hsv_min, hsv_max)
  # #腐蚀膨胀
  erode=cv2.erode(mask,None,iterations=1)
  dilate=cv2.dilate(erode,None,iterations=1)
#     cv2.imshow('res',dilate)
  rows,cols,channels = img.shape
  #遍历替换
  for i in range(rows):
      for j in range(cols):
          if dilate[i,j]==255:
              img[i,j]=(color_list[0],color_list[1],color_list[2])#此处替换颜色,为BGR通道
  new_path='%s_%s.jpg'%(str(int(time.time())),color)
  path=path.replace(path.split('/')[-1],new_path)
  new_path=path
  #防止中文路径
  cv2.imencode('.jpg',img)[1].tofile(path)
#     cv2.imwrite(path,img)
#     cv2.imshow('res',img)
#     cv2.waitKey(0)
#     cv2.destroyAllWindows()

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.pushButton = QtWidgets.QPushButton(self.centralwidget)
      self.pushButton.setGeometry(QtCore.QRect(280, 0, 251, 331))
      self.pushButton.setObjectName("pushButton")
      self.pushButton.clicked.connect(self.openFile)
      
      self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
      self.pushButton_2.setGeometry(QtCore.QRect(140, 390, 101, 51))
      self.pushButton_2.setObjectName("pushButton_2")
      self.pushButton_2.clicked.connect(lambda:self.setimg_bg("blue"))
      
      self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)
      self.pushButton_3.setGeometry(QtCore.QRect(280, 390, 101, 51))
      self.pushButton_3.setObjectName("pushButton_3")
      self.pushButton_3.clicked.connect(lambda:self.setimg_bg("green"))
      
      self.pushButton_4 = QtWidgets.QPushButton(self.centralwidget)
      self.pushButton_4.setGeometry(QtCore.QRect(430, 390, 101, 51))
      self.pushButton_4.setObjectName("pushButton_4")
      self.pushButton_4.clicked.connect(lambda:self.setimg_bg("red"))
      
      self.pushButton_5 = QtWidgets.QPushButton(self.centralwidget)
      self.pushButton_5.setGeometry(QtCore.QRect(570, 390, 101, 51))
      self.pushButton_5.setObjectName("pushButton_5")
      self.pushButton_5.clicked.connect(lambda:self.setimg_bg("white"))
      
      MainWindow.setCentralWidget(self.centralwidget)
      self.menubar = QtWidgets.QMenuBar(MainWindow)
      self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 26))
      self.menubar.setObjectName("menubar")
      MainWindow.setMenuBar(self.menubar)
      self.statusbar = QtWidgets.QStatusBar(MainWindow)
      self.statusbar.setObjectName("statusbar")
      MainWindow.setStatusBar(self.statusbar)

      self.retranslateUi(MainWindow)
      QtCore.QMetaObject.connectSlotsByName(MainWindow)
      
  def openFile(self):
      global img_path
      get_filename_path, ok = QFileDialog.getOpenFileName()
      if ok:
          img_path=str(get_filename_path)
          self.pushButton.setStyleSheet("QPushButton{border-image: url(%s)}"%str(get_filename_path))
          
  def setimg_bg(self,color):
      global img_path,new_path
      if img_path != '':
          change_bg_color(img_path,color)
          self.pushButton.setStyleSheet("QPushButton{border-image: url(%s)}"%str(new_path))
          
  def retranslateUi(self, MainWindow):
      _translate = QtCore.QCoreApplication.translate
      MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
      self.pushButton.setText(_translate("MainWindow", ""))
      self.pushButton_2.setText(_translate("MainWindow", "蓝色"))
      self.pushButton_3.setText(_translate("MainWindow", "绿色"))
      self.pushButton_4.setText(_translate("MainWindow", "红色"))
      self.pushButton_5.setText(_translate("MainWindow", "白色"))

if __name__ == '__main__':
  global img_path,new_path
  img_path=''
  app = QApplication(sys.argv)
  MainWindow = QMainWindow()
  ui = Ui_MainWindow()
  ui.setupUi(MainWindow)
  MainWindow.show()
  sys.exit(app.exec_())

小结:在网络上下载不同底色的图片实验了几十次,发现白底穿白色衣服时直接将身体也被更换的底色覆盖了,所以至始至终都是超级简陋的换底,还是removebg这些经过学习的好,连发丝间都能更换,看起来比较细腻顺滑,不过在此过程中也学到了一些图片处理的知识,opencv默认是BGR的,而有些库是RGB的,所以在判断底色的时候一开始我是使用hsv,发现行不通,后面只好使用BGR提取色块来判断。

 

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程宝库

 ProtoBuf 协议gRPC使用 protocol buffer 协议做为接口描述语言(IDL) ,来定义接口及传递的消息数据类型。gRPC的message 与 service 均须 ...