ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

计算机视觉 --基于opencv实现证件照换底色、翻转

2021-03-06 12:57:17  阅读:340  来源: 互联网

标签:img -- cv2 opencv np array 证件照 imgArray 255


1. 原因

在实际工作生活中,需要根据各种要求,提供不同底色的证件照电子版。常见的证照件底色有红底,蓝底,白底。

但我们在大部分情况只有其中一种底色的照片,就需要通过技术手段实现证件照换底色。但因为PS技术不到位,有瑕疵,通常处理的效果不理想。

本文基于opencv-python,  通过代码的方式实现y证件照底色的替换,同时针对相关的函数如图片缩放、翻转的使用方式进行学习。

2.图片来源及换底效果展示

2.1 图片来源说明

该图片来源于百度图片,如果侵权,请联系我删除!图片仅用于知识交流学习。

2.2 换底及翻转效果展示

将证件照的底色从蓝底换成白底,下面展示了图片的左右翻转

3. 源代码及相关知识介绍

3.1 相关知识介绍

3.1.1 cvtColor 颜色空间转换函数

cvtColor函数的定义是: def cvtColor(src, code, dst=None, dstCn=None):  用于将图片的颜色空间进行转换

注意: cv2.imread读取图片,是按BGR颜色空间读取的,不是最常见的RGB格式 ,在进行证件照底色变换时,需要将图片的颜色空间转换为HSV

即hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

3.1.2 inRange

inRange函数的定义是: def inRange(src, lowerb, upperb, dst=None):, 用于将图片中介于 lower/upper 之间的为变为白色,其余的变为黑色,效果如下:

函数参数说明:

  • 第一个参数:src指的是原图
  • 第二个参数:lowerb指的是图像中低于这个 lower_red 的值,图像值变为 0
  • 第三个参数:upperb指的是图像中高于这个 upper_red 的值,图像值变为 0
  • 第四个参数:dst 指输出,一般不指定。

    注意: 在 lower_red~upper_red 之间的值变成 255, 注意:不同的证件照底色,inRange的lowerb,upperb需要根据HSV色彩空间表进行设置

3.1.3 HSV颜色模型

HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型(Hexcone Model)。

是将RGB色彩空间中的点在倒圆锥体中的表示方法,HSV即:色调(H),饱和度(S),明度(V)

色调H

       用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,紫色为300°;

饱和度S

       饱和度S表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。

明度V

       明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白)。

HSV模型的三维表示从RGB立方体演化而来。设想从RGB沿立方体对角线的白色顶点向黑色顶点观察,就可以看到立方体的六边形外形。六边形边界表示色彩,水平轴表示纯度,明度沿垂直轴测量。HSV颜色空间可以用一个圆锥空间模型来描述。

圆锥的顶点处,V=0,H和S无定义,代表黑色。圆锥的顶面中心处V=max,S=0,H无定义,代表白色。见下图:

                                 

HSV色彩空间表: 在源代码中基于HSV色彩空间表的数据,放在colorLowerB、colorUpperB,用于根据指定的颜色,产生inRange函数的lowerb,upperb参数

详见请参见:OpenCV学习笔记——HSV颜色空间超极详解&inRange函数用法及实战

3.2 源代码

     证件照换底色,使用:changeImg = imageProcess.changeBackground(img1, 'blue', 'white'), 第二个参数是当前照片的底色,第三个照片是想向换成的底色

import cv2
import numpy as np
from PIL import ImageFont

class ImageProcess:
    # 用于产生图片的mask遮罩,在cv2.inRange中使用, 效果是将证件底的底色变白色
    colorLowerB = {
        'red': np.array([156, 43, 46]),
        'orange': np.array([11, 43, 46]),
        'yellow': np.array([26, 43, 46]),
        'green': np.array([35, 43, 46]),
        'cyan': np.array([78, 43, 46]),
        'blue': np.array([100, 43, 46]),
        'purple': np.array([125, 43, 46]),
        'black': np.array([0, 0, 0]),
        'gray': np.array([0, 0, 46]),
        'white': np.array([0, 0, 221]),
    }

    colorUpperB = {
        'red': np.array([180, 255, 255]),
        'orange': np.array([25, 255, 255]),
        'yellow': np.array([34, 255, 255]),
        'green': np.array([77, 255, 255]),
        'cyan': np.array([99, 255, 255]),
        'blue': np.array([124, 255, 255]),
        'purple': np.array([155, 255, 255]),
        'black': np.array([180, 255, 46]),
        'gray': np.array([180, 43, 220]),
        'white': np.array([180, 30, 255]),
    }
    # BGR通道
    destColor = {
        'red': (0, 0, 255),
        'orange': (0, 165, 255),
        'yellow': (0, 255, 255),
        'green': (0, 128, 0),
        'blue': (255, 0, 0),
        'cyan': (255, 255, 0),
        'purple': (128, 0, 128),
        'black': (0, 0, 0),
        'gray': (128, 128, 128),
        'white': (255, 255, 255),
    }

    def __init__(self, scale=1):
        self.scale = scale

    def changeBackground(self, img, originColor='white', changeColor='blue'):
        originImg = self.copyImg(img)
        rows, cols, channels = img.shape

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

        # 图片的二值化处理
        lowerB = ImageProcess.colorLowerB.get(originColor)
        upperB = ImageProcess.colorUpperB.get(originColor)
        mask = cv2.inRange(hsv, lowerB, upperB)

        # 针对图片进行腐蚀膨胀
        kernel = np.ones((3, 3), np.uint8)
        # kernel = None
        erode = cv2.erode(mask, kernel, iterations=1)
        dilate = cv2.dilate(erode, kernel, iterations=1)

        # 遍历每个像素点,进行颜色的替换
        print(type(ImageProcess.destColor))
        tmpColor = ImageProcess.destColor.get(changeColor)
        print(tmpColor)
        for i in range(rows):
            for j in range(cols):
                if dilate[i, j] == 255:  # 像素点为255表示的是白色,我们就是要将白色处的像素点,替换为红色
                    img[i, j] = tmpColor  # 此处替换颜色,为BGR通道,不是RGB通道
        return img

    def copyImg(self, img):
        '''
        复制图像
        :param img:
        :return:
        '''
        return cv2.cvtColor(img, cv2.NORMAL_CLONE)

    def flipImg(self, img):
        '''
        翻转图像
        :param img:
        :return:
        '''
        rows, cols = img.shape[:2]
        mapx = np.zeros(img.shape[:2], np.float32)
        mapy = np.zeros(img.shape[:2], np.float32)
        for i in range(rows):
            for j in range(cols):
                mapx.itemset((i, j), cols - 1 - j)
                mapy.itemset((i, j), i)

        return cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)

    def showMultiPics(self, imgArray):
        rows = len(imgArray)
        cols = len(imgArray[0])

        # 第一张图片的宽高
        width = imgArray[0][0].shape[1]
        height = imgArray[0][0].shape[0]

        for x in range(0, rows):
            for y in range(0, cols):
                # 遍历,如果是第一幅图像
                if imgArray[x][y].shape[:2] == imgArray[0][0].shape[:2]:
                    imgArray[x][y] = cv2.resize(imgArray[x][y], (0, 0), None, self.scale, self.scale)
                # 将其他矩阵变换为与第一幅图像相同大小,缩放比例为scale
                else:
                    imgArray[x][y] = cv2.resize(imgArray[x][y],
                                                (width, height), None, self.scale,
                                                self.scale)
                # 如果图像是灰度图,将其转换成彩色显示
                if len(imgArray[x][y].shape) == 2:
                    imgArray[x][y] = cv2.cvtColor(imgArray[x][y], cv2.COLOR_GRAY2BGR)

        # 创建一个空白画布,与第一张图片大小相同
        imgBlank = np.zeros((height, width, 3), np.uint8)
        hor = [imgBlank] * rows
        for x in range(0, rows):
            # 将元组里第x个列表水平排列
            hor[x] = np.hstack(imgArray[x])
        ver = np.vstack(hor)  # 将不同列表垂直拼接
        return ver


if __name__ == '__main__':
    imageProcess = ImageProcess()
    img1 = cv2.imread('zjz1.jpg')
    img2 = cv2.imread('zjz1.jpg')

    #缩放图片
    img1 = cv2.resize(img1, None, fx=0.5, fy=0.5)
    img2 = cv2.resize(img1, None, fx=0.5, fy=0.5)

    originImg1 = imageProcess.copyImg(img1)
    originImg2 = imageProcess.copyImg(img2)

    flipImg = imageProcess.flipImg(img2)
    changeImg = imageProcess.changeBackground(img1, 'blue', 'white')

    font = cv2.FONT_HERSHEY_SIMPLEX
    cv2.putText(originImg1, 'origin-pic', (0, 15), font, 0.5, (0, 0, 0), 1)
    cv2.putText(originImg2, 'origin-pic', (0, 15), font, 0.5, (0, 0, 0), 1)
    cv2.putText(flipImg, 'pic flip', (0, 15), font, 0.5, (0, 0, 0), 1)
    cv2.putText(changeImg, 'pic change background color', (0, 15), font, 0.5, (0, 0, 0), 1)

    cv2.imshow("show multi pics", imageProcess.showMultiPics(([originImg1, changeImg], [originImg2, flipImg])))

    # 窗口等待的命令,0表示无限等待
    cv2.waitKey(0)
    cv2.destroyAllWindows()

3.3 代码存在的问题

    如果证件照的底色在人脸,头发或衣服等区域存在,则这些地方也会被换颜色,造成效果不好。

 

标签:img,--,cv2,opencv,np,array,证件照,imgArray,255
来源: https://blog.csdn.net/penriver/article/details/114436022

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有