大数据文摘编辑组出品技术实现:宁静 中秋节,直男送礼,首选口红。毕竟李佳琦一句"OMG买它”,女朋友披头散发抢购,钱包就空了一半。但是,口红色号千千万,选对了牌子才成功了一半。快乐橙、伤心紫,姨妈红,鸡屎绿…直男眼里没什么区别的颜色,在女生眼里各种色调、质地细微的区别都能分析一清二楚。那么,对于直男来说,怎么才能搞清楚如此多的口红色号呢?文摘菌耗费一毫米发际线,琢磨了一下,做出了一个口红色号识别器,希望能帮大家在七夕把深刻的革命友谊再升华一下。先来看看效果。让我们假设,中秋前夕,小姐姐发来了一张美妆博主的美照,并暗示你,“人家也喜欢这个颜色。”图片来自网络这个时候,用我们的口红色号识别器,就能定位嘴唇,并迅速给出它的颜色隶属哪家品牌的哪个色号。OMG!简直比李佳琦还准确!好啦,废话不多说,马上开始教学时间!

来自Github的口红色号宇宙

要想识别口红色号,先得让机器知道到底都有哪些颜色。文摘菌听柜姐介绍,红色系有:“草莓红、铁锈红、枫叶红...”,其他还有“豆沙色、吃土色、番茄色...”世界观还未建立完全就要开始土崩瓦解,这看着有区别吗?“豆沙色最为百搭,橘调的番茄色比较显白...”眼前的黑不是黑,你说的红是什么红?还好,在万能的github上,文摘菌找到了一个宝藏数据库“口红颜色可视化”,这个数据库堪比口红的色号宇宙,不仅囊括了当前最主流品牌的各种系列色号,还很良心的在色盘上排列了出来。这个数据集是一个嵌套的字典数据结构,存为json串的形式,里面记录了每个口红品牌系列下不同口红色号的颜色id、名称、和16进制颜色值。直!男!救!星!有木有!口红色号可视化链接:

https://github.com/Ovilia/lipstick

不过看这这密密麻麻的颜色,真心佩服各大口红品牌的文案高手,是怎么样区别每一个看不出区别的颜色,并且还要分别取名字的。傻傻分不清的文摘菌对5个品牌的不同系列做了一下统计和色号录入,于是,剩下的就交给计算机啦。

先用番茄做个实验?

既然有了如此完备的色号数据库,那么文摘菌就有了一个讨巧的方法:要想找到合适的色号,可以直接截取颜色,然后在数据库中进行比对。这个方法非常好操作,在上唇色之前,我们不如先拿别的红色物品来练手。比如,这里有一只番茄图片。你看这个番茄它又大又圆:文摘菌在其中截取了成色均匀、无高亮的矩形图片:提取这张纯色图片的RGB值在技术上是可行的,getcolor.py代码如下:

import colorsysimport PIL.Image as Image def get_dominant_color(image):    max_score = 0.0001    dominant_color = None    for count,(r,g,b) in image.getcolors(image.size[0]*image.size[1]):        # 转为HSV标准        saturation = colorsys.rgb_to_hsv(r/255.0, g/255.0, b/255.0)[1]        y = min(abs(r*2104+g*4130+b*802+4096+131072)>>13,235)        y = (y-16.0)/(235-16)         #忽略高亮色        if y > 0.9:            continue        score = (saturation+0.1)*count        if score > max_score:            max_score = score            dominant_color = (r,g,b)    return dominant_color

为了减少误差,需要裁剪多个不同位置的图片,保存在本地的一个文件夹中,读取文件,提取颜色,求平均值,得到的番茄最终的RGB颜色,代码如下:

import osimport getcolorfrom os.path import join as pjoinfrom scipy import miscdef load_color(color_dir,list):     count = 0    for dir in os.listdir(color_dir):          img_dir = pjoin(color_dir, dir)          image = getcolor.Image.open(img_dir)        image = image.convert('RGB')        get=getcolor.get_dominant_color(image)        list.append(get)        count = count+1        #print(person_dir)    #print(count)    return countdef Mean_color(count,list):     Mean_R=Mean_G=Mean_B=0     for i in range(count):        tuple=list[i]        Mean_R+=tuple[0]        Mean_G+=tuple[1]        Mean_B+=tuple[2]     MeanC=((int)(Mean_R/count),(int)(Mean_G/count),(int)(Mean_B/count))     return Me

番茄的颜色提取到了,那么和什么做比对呢?当然是口红的数据,文摘菌这儿用到了5个品牌,分别是圣罗兰、香奈儿可可小姐、迪奥、美宝莲、纪梵希,共17个系列,271个口红色号,数据集是一个嵌套的字典数据结构,存为json串的形式,里面记录了每个口红品牌系列下不同口红色号的颜色id、名称、和16进制颜色值,lipstick.json部分数据集展示如下:

{"brands":[{"name":"圣罗兰","series":[{"name":"莹亮纯魅唇膏","lipsticks":[{"color":"#D62352","id":"49","name":"撩骚"},{"color":"#DC4B41","id":"14","name":"一见倾心"},{"color":"#B22146","id":"05","name":"浮生若梦"},

数据集中存储的RGB颜色是16进制的字符串形式,需要将其转换成RGB值,比较两个颜色相近与否,实际上是比较RGB三个分量维度上的误差,最小的口红输出对应的品牌、系列、色号和id,代码如下:

import jsonimport getcolorimport numpy as npimport lipcolor#filename = 'temp.txt'##write the temp data to file##def WtoFile(filename,RGB_temp):    num=len(RGB_temp)    with open(filename,'w') as f:        for i in range(num):           s = str(RGB_temp[i]).replace('[','').replace(']','')           f.write(s)           f.write("\n")           #operate the data ###save the brand&series&color id&color name to sum_list####covert the color #D62352 to RGB_array####caculate the RGB difference to RGB_temp and write the value to file##def data_operate():    with open('lipstick.json', 'r', encoding='utf-8') as f:      ret_dic = json.load(f)      #print(ret_dic['brands'])      #print(type(ret_dic)) #       #print(ret_dic['brands'][0]['name'])       b_num=len(ret_dic['brands'])      #print(b_num)#brands number          s_list=[]      #series brands#      for i in range(len(ret_dic['brands'])):          s_num=len(ret_dic['brands'][i]['series'])          s_list.append(s_num)          #print("{0} has {1} series".format((ret_dic['brands'][i]['name']),(s_list[i])))              #the lipstick color of every brands every series#      #the first loop calculate the total color numbers      sum=0      for b1 in range(b_num):          for s1 in range(s_list[b1]):              brand_name=ret_dic['brands'][b1]['name']              lip_name=ret_dic['brands'][b1]['series'][s1]['name']              color_num=len(ret_dic['brands'][b1]['series'][s1]['lipsticks'])              sum+=color_num#calculate the total color numbers                  #the second loop save the message to a list#      sum_list=np.zeros((sum,4), dtype=(str,8))      value_array=np.zeros((sum,6), dtype=int)      i=0          for b2 in range(b_num):          for s2 in range(s_list[b2]):              brand_name=ret_dic['brands'][b2]['name']              #print(type(brand_name))              lip_name=ret_dic['brands'][b2]['series'][s2]['name']              color_num=len(ret_dic['brands'][b2]['series'][s2]['lipsticks'])              for c in range(color_num):                    color_value=ret_dic['brands'][b2]['series'][s2]['lipsticks'][c]['color']                    color_name=ret_dic['brands'][b2]['series'][s2]['lipsticks'][c]['name']                    color_id=ret_dic['brands'][b2]['series'][s2]['lipsticks'][c]['id']                    #print("{0} series {1} has {2} colors,color {3}:{4}".format(brand_name,lip_name,color_num,c+1,color_name))                    sum_list[i][0]=brand_name                    sum_list[i][1]=lip_name                    sum_list[i][2]=color_id                    sum_list[i][3]=color_name                    #value_array[i]=value_array[i][1]                    #convert "#D62352" to [13  6  2  3  5  2]#                    for l in range(6):                        temp=color_value[l+1]                        if(temp>='A'and temp<='F'):                           temp1=ord(temp)-ord('A')+10                        else:                           temp1=ord(temp)-ord('0')                        value_array[i][l]=temp1                    i+=1                                        #the third loop covert value_array to RGB_array#      RGB_array=np.zeros((sum,3), dtype=int)      for i in range(sum):          RGB_array[i][0]=value_array[i][0]*16+value_array[i][1]          RGB_array[i][1]=value_array[i][2]*16+value_array[i][3]          RGB_array[i][2]=value_array[i][4]*16+value_array[i][5]          #calculate the similar and save to RGB_temp      #RGB_temp=np.zeros((sum,1), dtype=int)      RGB_temp=np.zeros((sum,1), dtype=float)      for i in range(sum):          R=RGB_array[i][0]          G=RGB_array[i][1]          B=RGB_array[i][2]          RGB_temp[i]=abs(get[0]-R)+abs(get[1]*3/4-G)+abs(get[2]-B)      RGB_temp.tolist();#covert array to list      #print(RGB_temp)      filename="temp.txt"      WtoFile(filename,RGB_temp)      #sort the RGB_temp#      result=sorted(range(len(RGB_temp)), key=lambda k: RGB_temp[k])      #print(result)      #output the three max prob of the lipsticks#      print("The first three possible lipstick brand and color id&name are as follows:")      for i in range(3):          idex=result[i]          print(sum_list[idex])      print("The first three possible lipstick brand RGB value are as follows:")      for i in range(3):          idex=result[i]          R=RGB_array[idex][0]          G=RGB_array[idex][1]          B=RGB_array[idex][2]          tuple=(R,G,B)          print(tuple)if __name__ == '__main__':     #image = getcolor.Image.open(inputpath)     #image = image.convert('RGB')     #get=getcolor.get_dominant_color(image)#tuple #get=(231, 213, 211)     list=[]     color_dir="output"     count=lipcolor.load_color(color_dir,list)     get=lipcolor.Mean_color(count,list)     print("the extracted RGB value of the color is {0}".format(get))     #operate the data#     data_operat

文摘菌输出了最有可能吻合番茄颜色的前三个口红的信息,在Spyder中的运行结果:可以看到最有可能的三个口红品牌色号的RGB值与番茄的RGB值是非常接近的:提取到的番茄颜色:'迪奥' '烈艳蓝金唇膏' '080' '微笑正红’的颜色:'圣罗兰' '纯口红' '56' '橙红织锦'的颜色:'纪梵希' '高定香榭天鹅绒唇' '325' '圣水红'的颜色:文摘菌已经眼花缭乱,三个颜色……有区别吗?!以后不如准备统一叫它们,番茄色!不过,这也正说明了,刚刚的提取&对比方法可行!既然可以识别番茄的颜色,那么,可以识别人像中的口红色号吗?进入正题!人像口红色号识别接下来,我们需要做的是输入一张人像图片,可以自动识别其中的嘴唇区域,并提取出嘴唇区域中的一部分做为颜色提取的源图像。这里就要用到CV的人脸识别了,还好Dlib库有帮助我们减轻一大部分的工作量,Dlib中有自带的68个人脸的识别器,可以得到人脸部位包括眉毛、眼睛、鼻梁、面部轮廓和嘴唇区域的具体点的位置,到这儿,文摘菌以为很轻松就可以截到嘴唇区域了,结果有点尴尬.........我们首先找到了一张小姐姐的照片:截取到的嘴唇区域如下:很明显的看到上下嘴唇黑色的区域也截取到了,这对后续的提色有影响,所以文摘菌不得不回到最初的68个检测点来思考人生。

圣罗兰官网#842C71口红

标记的68个人脸检测点如上图所示,而嘴唇部位是从第49个标记点开始的(数组的话,下标是48),为了尽可能的截取到均匀成色的嘴唇片段,文摘菌刚开始是想从第50个标记点对角线截取到第56个标记点,而这不可避免的会截取到上下嘴唇之间的缝隙,这儿的阴影也会影响后续的颜色提取准确度,考虑到下嘴唇比上嘴唇宽,所以文摘菌截取到下嘴唇中间的两个小正方形区域:人脸识别和截取嘴唇区域的代码如下:

import numpy as np import cv2import dlibfrom PIL import Imagedef crop(source,pos):      x1=pos[2][0]      y1=pos[2][1]      x2=pos[1][0]      y2=pos[1][1]      d=abs(x2-x1)      region = source[(int)(y1-d*0.75):y2,x1:x2]      # save the image      cv2.imwrite("output/Mouth1.jpg", region)            x1=pos[1][0]      y1=pos[1][1]      x2=pos[0][0]      y2=pos[0][1]      d=abs(x1-x2)      region = source[y1-d:y2,x1:x2]      # save the image      cv2.imwrite("output/Mouth2.jpg", region)def detect_mouth(img,pos):        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)        gray = cv2.equalizeHist(gray)        detector = dlib.get_frontal_face_detector()        #use the predictor         predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')        dets = detector(img, 1)           print("Number of faces detected: {}".format(len(dets)))        for a in dets:               cv2.rectangle(img,(a.left(),a.top()),(a.right(),a.bottom()),(255,0,0))        #point_list=[]#save the mouth point to point_list[]#        #Extract 68 feature points of the face and crop the lip image#        for index, face in enumerate(dets):           print('face {}; left {}; top {}; right {}; bottom {}'.format(index, face.left(), face.top(), face.right(), face.bottom()))           shape = predictor(gray, face)           for i, pt in enumerate(shape.parts()):            #print('Part {}: {}'.format(i, pt))            #print(i)             pt_pos = (pt.x, pt.y)             if i>=48 and i<=67:                cv2.circle(img, pt_pos, 2, (255, 0, 0), 1)             if i>=56 and i<=58:                #print(pt_pos)                pos[i-56][0]=pt.x                pos[i-56][1]=pt.y             #cv2.circle(img, pt_pos, 2, (255, 0, 0), 1)        return img    if __name__ == "__main__":       img = cv2.imread("test3.png")      #copy the input image for the later crop#      img_clone = np.copy(img)      cv2.imwrite("input/source.jpg",img_clone)      #save the lip position to pos array#      pos=np.zeros((3,2), dtype=int)      result=detect_mouth(img,pos)      cv2.imwrite("input/source2.jpg",result)      #crop the lip areas#      source = cv2.imread("input/source.jpg")      crop(source,pos)      # show the result      cv2.imshow('FaceDetect',result)      cv2.waitKey(0)       cv2.destroyAllWindow

既然已经截取到嘴唇的小矩形图像了,接下来的工作就和前面一样了,在数据库中对比每个RGB值输出最小误差对应的口红信息,而这儿也有难到文摘菌,单纯的比对RGB分量对口红色号来说并不适用,有可能每个分量相差很小,而叠加起来的颜色和提取到的颜色并不相似,在颜色的比对上需要手动调参。几经波折,最后输出的结果还是可以接受的,上图人像中涂的口红色号,感兴趣的读者可以查下正好是下面输出排名第一的口红信息。

误差分析

对于我们测试的图片信息,文摘菌标记了嘴唇区域的特征点,我们提取到的RGB值(156,59,103)颜色如下所示:可以看到和图片的颜色已经十分接近了,而数据集合lipstick.json中这种口红存储的16进制颜色值为#842C71,对应的颜色如下:明显看到数据集存储的颜色和实际照片的颜色是有些许误差的,而在本文算法实现过程中,又不可避免的有以下误差:

  1. 嘴唇区域截取不可避免会截取到皮肤中的一部分颜色,虽然算法已经将那种可能降到最低;
  2. 颜色提取上,虽然截取多个嘴唇图片求平均值,但是本身的提取算法还是和实际值稍有偏差;
  3. RGB颜色相似度比对的算法也不够精确;
  4. 最最重要的是,照片必须是原图,而且光线要自然,加了滤镜的图是怎么也不可能识别出来的。

以上种种,使得让计算机快速高效地识别不同的口红色号还是有困难的,原来计算机有时候也会很直男。

文末福利:实时人像口红色号预测

看到这儿,可能很多读者朋友想实时地试一下能不能让计算机判断自己的口红色号,这对于OpenCV这一强大的图形操作库来说,不是什么问题,它可以打开你的摄像头,读取每一帧的图片,结合前文提到的人脸识别代码,可以实时地截取到嘴唇区域的图片,然后交给计算机预测,从此再也不怕女朋友的灵魂拷问!最后,附上打开摄像头的代码,快叫女朋友过来试下吧!

#coding=utf8import cv2import timeprint('Press Esc to exit')imgWindow = cv2.namedWindow('FaceDetect', cv2.WINDOW_NORMAL)import sysimport osimport dlibimport globimport numpyfrom skimage import iodef detect_face():    capInput = cv2.VideoCapture(0)    #nextCaptureTime = time.time()    faces = []    feas = []     if not capInput.isOpened(): print('Capture failed because of camera')    while 1:        ret, img = capInput.read()        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)        gray = cv2.equalizeHist(gray)        time=0        eTime = time.time() + 0.1        detector = dlib.get_frontal_face_detector()         predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')        dets = detector(gray, 1)          print("Number of faces detected: {}".format(len(dets)))        for a in dets:               cv2.rectangle(img,(a.left(),a.top()),(a.right(),a.bottom()),(255,0,0))        for index, face in enumerate(dets):           print('face {}; left {}; top {}; right {}; bottom {}'.format(index, face.left(), face.top(), face.right(), face.bottom()))           shape = predictor(gray, face)              for i, pt in enumerate(shape.parts()):            #print('Part {}: {}'.format(i, pt))             pt_pos = (pt.x, pt.y)             cv2.circle(img, pt_pos, 2, (255, 0, 0), 1)        cv2.imshow('FaceDetect',img)        if cv2.waitKey(1) & 0xFF == 27: break    capInput.release()    cv2.destroyAllWindows()if __name__ == "__main__":     detect_face()

好啦,佳期如梦,双星良夜,在这个充满爱意的日子里,定位好女神常用的口红色号,和那个她来场华丽的邂逅吧!不说了,文摘菌去下单口红了!   互 动 留 言

中秋节,你想祝福谁呢?

 赠 书 彩 蛋 

参与方式:分享你的祝福@你想要的书籍编号

看留言真心与否,随机抽取发送!(奖池:12本)

听说点赞较多的容易中奖哦~~~

快转发朋友圈,邀请好友过来点赞吧

中奖名单将在9月16日,本文留言区公布

点「在看」的人都变好看了哦

潘通色号与rgb转换_中秋福利!手把手教你用Python做一只口红色号识别器,秒变李佳琦...相关推荐

  1. 用Python手把手教你做一只口红色号识别器,秒变李佳琦

    大数据文摘编辑组出品技术实现:宁静  七夕将至,送礼时节.直男送礼,首选口红. 毕竟李佳琦一句"OMG买它",女朋友披头散发抢购,钱包就空了一半. 但是,口红色号千千万,选对了牌子 ...

  2. 技术党求生骚操作!手把手教你做一只口红色号识别器!

    直男送礼,首选口红. 毕竟李佳琦一句"OMG买它",女朋友披头散发抢购,钱包就空了一半. 但是,口红色号千千万,选对了牌子才成功了一半. 快乐橙.伤心紫,姨妈红,鸡屎绿-直男眼里没 ...

  3. 直男福利!手把手教你做一只口红色号识别器,秒变李佳琦

    大数据文摘编辑组出品技术实现:宁静 七夕将至,送礼时节.直男送礼,首选口红.毕竟李佳琦一句"OMG买它",女朋友披头散发抢购,钱包就空了一半.但是,口红色号千千万,选对了牌子才成功 ...

  4. python打字机效果_零基础手把手教你用Python做一个怀旧打字机

    忙碌的生活和飞速发展的科技正改变着人们的阅读习惯,为了"增加效率",文章只看标题,视频10秒刷走.是不是有这么一种感觉,一个小时内看遍世间繁华

  5. 红头文件rgb红色值_拿下抖音小姐姐,我写了个口红色号识别器

    对于广大"钢铁直男"的程序员来说,送什么礼物给女朋友一直是个世纪难题. 其实哄女朋友开心最深的套路就是花式送口红,就问谁抵挡得住啊啊啊啊...... "没有什么问题是一支 ...

  6. 小米电视广告_教程福利 | 手把手教你去除小米电视/小米盒子的内置广告!

    视频:当马东给雷军看他自己的鬼畜是什么反应? (⊙_⊙) 精 品 资 源 分 享 微信公众号:手机软件资源局 NO.19103 - 去除小米盒子电视广告 编 辑:文 娜 小米作为新国货的代表,这些年给 ...

  7. python控制灯泡_人工智能应用-手把手教你用Python硬件编程实现打开或关闭电灯泡...

    之前我们已经给广大爱好者或程序员朋友们,带来了硬件版的或者说物联网版本的Hello World C++Builder版.Delphi.Visual Basic.Net等的程序源码和教学资料,让大家对硬 ...

  8. 口红试色app开发,轻松找到满意的口红色号

    口红试色app开发,轻松找到满意的口红色号.口红试色app上有口红资讯的功能.口红资讯上有很多口红试色的内容,用户可以在这里学习和了解.因为每个人唇部状态的不同都会导致试色有一定的区别,如果不及时了解 ...

  9. python处理时间序列非平稳_手把手教你用Python处理非平稳时间序列

    简介 预测一个家庭未来三个月的用电量,估计特定时期道路上的交通流量,预测一只股票在纽约证券交易所交易的价格--这些问题都有什么共同点? 它们都属于时间序列数据的范畴!如果没有"时间" ...

最新文章

  1. ubuntu15.10避免图形界面无法登录的jdk配置
  2. java什么是稳定排序,这可能是你听说过最快的稳定排序算法
  3. 梅捷SY-A780G+冷启动黑屏,需Reset才能进入系统
  4. FM,FFM及其实现
  5. 修改服务器时间报错,修改服务器时间linux
  6. Java基础入门笔记-添加包
  7. Spring Cloud 微服务实战系列-Spring Boot再次入门(一)
  8. python特性描述_详解 Python 最优雅的特性之一 — 描述符
  9. 素数环(nyoj488)
  10. Instant Run 的操作影响到了代码,导致Android App启动闪退的问题
  11. python读取、写入txt文本内容
  12. IDEA相对路径没有效果的问题
  13. iOS 数据归档解档
  14. php压缩文件夹(整理最新版)
  15. 神奇的平面几何定理--康威圆定理
  16. 计算机网络的最大优点,什么是计算机网络最突出的优点
  17. Java写入磁盘阵列_磁盘阵列方案
  18. Node.js 在微医的应用场景及实践
  19. python 实现MR
  20. SQL Server 教程

热门文章

  1. Proximal Policy Optimization Algorithms
  2. 点击唤起电话功能和企业微信聊天窗口事件(H5)
  3. 电脑商城-02-注册
  4. 将UTC(字符串包含TZ的时间)时间转换成本地时间 python
  5. 【Babel】1145- 非常不错的 Babel 插件开发教程
  6. Linux系统自动更新时间命令的详细说明
  7. C#进程间通信的几种方式:管道通信
  8. 【雷达】基于TI毫米波雷达IWR6843ISK实现室内人员检测附matlab代码
  9. 家有经济适用男牛仔很忙
  10. 二十、调度器、预选策略以及优选函数