![71dff87688cdca4eb8580d17696e7c03.gif](https://i-blog.csdnimg.cn/blog_migrate/ca215054113f3d4dfc6c75feb17d9459.gif)
![3198753e82d440effc2b2fde6541ae2e.gif](https://i-blog.csdnimg.cn/blog_migrate/a7902bccd8a1b15d669813ca1bd67b72.gif)
![9d3c891279f6a0d4e826923d5c00f973.png](https://i-blog.csdnimg.cn/blog_migrate/a2acd1faa94341930a550c4b2b7d0c5f.jpeg)
![dbe25c6e46f5b03283891cbe85ebf34e.png](https://i-blog.csdnimg.cn/blog_migrate/e93dcf2ffc6cc098c23449ad0cf72dc1.png)
![44f80bf408585b35fddf0962e505b6e3.png](https://i-blog.csdnimg.cn/blog_migrate/b4bebed2845ac76d69bc6ad3042fa3b0.png)
来自Github的口红色号宇宙
要想识别口红色号,先得让机器知道到底都有哪些颜色。 文摘菌听柜姐介绍,红色系有: “草莓红、铁锈红、枫叶红...”,其他还有“豆沙色、吃土色、番茄色...”![146c16cb54888d446ef698821dd1ffe0.gif](https://i-blog.csdnimg.cn/blog_migrate/18e41c589c7b00d5226fac1c16eb5405.gif)
![2e29c755bf5ba4a1d2794063164f6e12.png](https://i-blog.csdnimg.cn/blog_migrate/d037f846f82a5c767ead8b3dae59c008.png)
https://github.com/Ovilia/lipstick
不过看这这密密麻麻的颜色,真心佩服各大口红品牌的文案高手,是怎么样区别每一个看不出区别的颜色,并且还要分别取名字的。![17930360a391c7cb38233669b588b80b.png](https://i-blog.csdnimg.cn/blog_migrate/774a5a3c950753b9bf3edca439c5689b.jpeg)
先用番茄做个实验?
既然有了如此完备的色号数据库,那么文摘菌就有了一个讨巧的方法: 要想找到合适的色号,可以直接截取颜色,然后在数据库中进行比对。 这个方法非常好操作,在上唇色之前,我们不如先拿别的红色物品来练手。 比如,这里有一只番茄图片。 你看这个番茄它又大又圆:![c043fa836d4c78912944436f7c59343d.png](https://i-blog.csdnimg.cn/blog_migrate/43de00cc0cb5b5062a07c11a6731cd58.jpeg)
![58b06949af9ae91bf89acc0a476aa573.png](https://i-blog.csdnimg.cn/blog_migrate/22b0725bef649b04ba73136695dc5cb7.jpeg)
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中的运行结果:
![77bcf0030084299d488b6201f269d9ae.png](https://i-blog.csdnimg.cn/blog_migrate/362935f9cf2c0b13ccb71cf005d62387.png)
![4b1f8440d4e1c5eb49ad2e4a116127c9.png](https://i-blog.csdnimg.cn/blog_migrate/43cbd4153191df3d6bb2ee76c97ddcd1.png)
![6e7fae311aec64547a2221c13f9acdc5.png](https://i-blog.csdnimg.cn/blog_migrate/aeebb67a37634d81e0f903d3ff2be062.png)
![1296a8616d105e5af32a0248bb2502eb.png](https://i-blog.csdnimg.cn/blog_migrate/2de2ce06832da5395d47babb2a80f0a1.png)
![c5f70d993c0de74083444eec9b6b70fb.png](https://i-blog.csdnimg.cn/blog_migrate/4d51d59930df3ce8110967fe291eb55b.png)
![bf5f8ddd77df38e80b7f8431bd9784ac.png](https://i-blog.csdnimg.cn/blog_migrate/85ff0abfcd823e2184f178ebd55ff92d.png)
![1cc7d6be2af5f9ad0a82bbf4df739209.png](https://i-blog.csdnimg.cn/blog_migrate/95741d9c1025b507f7741388584373a0.jpeg)
![7e4fc644b3041ccd433775eb2ef18b2b.png](https://i-blog.csdnimg.cn/blog_migrate/510c244387fe93a00a781e5026e6350d.jpeg)
![554e49e7bab730ba8376d4591f360d21.png](https://i-blog.csdnimg.cn/blog_migrate/63c30c02ccfb565d2da1a162cff1d055.jpeg)
圣罗兰官网#842C71口红
标记的68个人脸检测点如上图所示,而嘴唇部位是从第49个标记点开始的(数组的话,下标是48),为了尽可能的截取到均匀成色的嘴唇片段,文摘菌刚开始是想从第50个标记点对角线截取到第56个标记点,而这不可避免的会截取到上下嘴唇之间的缝隙,这儿的阴影也会影响后续的颜色提取准确度,考虑到下嘴唇比上嘴唇宽,所以文摘菌截取到下嘴唇中间的两个小正方形区域:![462a89b93859f76d6c54effe5f8bfab2.png](https://i-blog.csdnimg.cn/blog_migrate/3d1e0e0ceb2633622e8e4f8119d26988.jpeg)
![1fbde071e186e024bf12439e49623ba0.png](https://i-blog.csdnimg.cn/blog_migrate/0a77f54c0f8c8ae794358f4bbfc6c73b.jpeg)
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分量对口红色号来说并不适用,有可能每个分量相差很小,而叠加起来的颜色和提取到的颜色并不相似,在颜色的比对上需要手动调参。
几经波折,最后输出的结果还是可以接受的,上图人像中涂的口红色号,感兴趣的读者可以查下正好是下面输出排名第一的口红信息。
![cbbb385ff0ed796a6fbd3183b5235468.png](https://i-blog.csdnimg.cn/blog_migrate/b8c2fd8dea4004ce4a7fc74bc25ce307.png)
误差分析
![45ca16c8eecc6aa42bd2023f1a26b513.png](https://i-blog.csdnimg.cn/blog_migrate/58c841740cf632a8c867e394efe06a5a.jpeg)
![a225bd17397fe674554bafcd6579fb84.png](https://i-blog.csdnimg.cn/blog_migrate/c483fcc9fd40afb88696292834523116.png)
![2fe01e71a7843be68e424e5e8663d636.png](https://i-blog.csdnimg.cn/blog_migrate/b2c8270fb3310c6b27c3d24fa93c31d6.png)
- 嘴唇区域截取不可避免会截取到皮肤中的一部分颜色,虽然算法已经将那种可能降到最低;
- 颜色提取上,虽然截取多个嘴唇图片求平均值,但是本身的提取算法还是和实际值稍有偏差;
- RGB颜色相似度比对的算法也不够精确;
- 最最重要的是,照片必须是原图,而且光线要自然,加了滤镜的图是怎么也不可能识别出来的。
文末福利:实时人像口红色号预测
看到这儿,可能很多读者朋友想实时地试一下能不能让计算机判断自己的口红色号,这对于OpenCV这一强大的图形操作库来说,不是什么问题,它可以打开你的摄像头,读取每一帧的图片,结合前文提到的人脸识别代码,可以实时地截取到嘴唇区域的图片,然后交给计算机预测,从此再也不怕女朋友的灵魂拷问!![d36658b4dccea1b9a0e231480efb73af.gif](https://i-blog.csdnimg.cn/blog_migrate/638d70207d5ff6fbe12d1b1f2fe6ff3d.gif)
#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()
好啦,佳期如梦,双星良夜,在这个充满爱意的日子里,定位好女神常用的口红色号,和那个她来场华丽的邂逅吧!
不说了,文摘菌去下单口红了!
![c30d28a39fb56846c05a9a73ad3cc59c.gif](https://i-blog.csdnimg.cn/blog_migrate/cdd912f01b0e3322587538db508f5474.gif)
![c5b1befebbba324d149e43d63e27f923.png](https://i-blog.csdnimg.cn/blog_migrate/95570945d045f153b29f420a0cf62fc8.jpeg)
中秋节,你想祝福谁呢?
![a3bc84007189aedd2fb60c7ada52040b.png](https://i-blog.csdnimg.cn/blog_migrate/e75366f37b0ef383067c787ff472484d.png)
参与方式:分享你的祝福@你想要的书籍编号
看留言真心与否,随机抽取发送!(奖池:12本)
听说点赞较多的容易中奖哦~~~
快转发朋友圈,邀请好友过来点赞吧
中奖名单将在9月16日,本文留言区公布
![20d9791daca956f588fd35bef104acda.png](https://i-blog.csdnimg.cn/blog_migrate/997c8caf08ede09159bc1de50e166add.png)