仅使用图像而不是通过标记

时间:2015-05-08 07:05:36

标签: opencv image-processing machine-learning computer-vision deep-learning

问题很简单,我希望在给出查询图像时找到类似的图像,类似于TinEye所做的。假设我有一件带有以下描述的衬衫

  

袖长:完整

     领子:礼物

     

pattern:striped

(以上数据只是为了让您感觉图像我实际上没有这些数据)

query similarOutput1 similaraOutput2 similarOutput3

第一个图像是查询图像,下一个图像应该是相似性查找算法的输出。 因此,基于该示例,我们具有灵活性,例如我们可以向用户显示具有改变颜色的图像,我们可以看到所有图像具有相同的图案,相同的领型或袖长。所以我必须显示视觉上相似的输出。

堆栈上也有类似的线程 link from stack不仅如此,还有很多其他的。但我对这种方法感到困惑。

在我的情况下,我不必搜索另一个类别,我必须搜索相同的类别,如果输入是衬衫我将只在衬衫类别中搜索。那部分已经完成。

所以问题是处理这个问题的方法是什么。 对于颜色来说这不是什么大问题。可以通过颜色直方图轻松提取颜色信息。让我们说输入是TShirt圆领,即没有领,半袖,并在文本中心打印。现在输出应该是类似半袖,圆领和中心印刷文字的图像。认为文字可能会有所不同。我尝试了K-Means聚类和P-hash但是没有用。请赐教我

PS:我必须找到类似的图像不重复。

1 个答案:

答案 0 :(得分:8)

我会尝试将此问题分解为3个较小的问题:

  • 检查图像是否显示长袖或短袖衬衫
  • 检查模式(规定,简单,别的?)
  • 确定衬衫的颜色

检查图像是否显示长袖或短袖衬衫
在我看来,这是最容易的。你提到你有类别名称,但是基于谷歌图形似乎衬衫或者TShirt是长袖还是短袖可能并不明显。 我的解决方案非常简单:

  • 在图像上找到面孔
  • 使用抓取算法从图像中提取面罩
  • 面具面(因此在此步骤之后只剩下面部 - 其他部分都是黑色)。请注意,此步骤不是必需的 - 我只提及它,因为它在最终图像上显示。
  • 将图片转换为HSV色彩空间
  • 使用面罩计算唯一的H和S颜色通道的直方图(没有图像的其余部分)
  • 使用上一步骤中的直方图计算hsv图像的反投影。谢谢你,你将只获得颜色(HSV)与脸部颜色相似的区域 - 所以你只能得到包含皮肤的区域。
  • 阈值结果(总有一些噪音:))

该算法的最终结果是显示皮肤区域的黑白图像。使用此图像,您可以计算皮肤像素数,并检查皮肤是仅在脸上还是在其他地方。您也可以尝试找到轮廓 - 通常两个解决方案都有机会检查手是否可见。是的 - 衬衫有短袖,没有长袖。

以下是结果(从左上角 - 原始图像,面罩(抓取算法的结果),蒙面,hsv图像,计算反投影的结果,使用前一个阈值的结果): enter image description here enter image description here enter image description here enter image description here
正如你所看到的,不幸的是它失败了图像3,因为脸部的颜色与衬衫图案非常相似(通常脸颜色非常接近白色 - 这个家伙出了问题,他应该花更多时间在外面;))。

来源很简单,但如果你不明白,可以随意问一下:

import cv2
import numpy as np


def process_image(img, face_pos, title):
    if len(face_pos) == 0:
        print 'No face found!'
        return
    mask = np.zeros((img.shape[0], img.shape[1]), dtype=np.uint8) #create mask with the same size as image, but only one channel. Mask is initialized with zeros
    cv2.grabCut(img, mask, tuple(face_pos[0]), np.zeros((1,65), dtype=np.float64), np.zeros((1,65), dtype=np.float64), 1, cv2.GC_INIT_WITH_RECT) #use grabcut algorithm to find mask of face. See grabcut description for more details (it's quite complicated algorithm)
    mask = np.where((mask==1) + (mask==3), 255, 0).astype('uint8') #set all pixels == 1 or == 3 to 255, other pixels set to 0
    img_masked = cv2.bitwise_and(img, img, mask=mask) #create masked image - just to show the result of grabcut
    #show images
    cv2.imshow(title, mask) 
    cv2.imshow(title+' masked', img_masked)

    img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) #convert image to hsv
    channels = [0,1]
    channels_ranges = [180, 256]
    channels_values = [0, 180, 0, 256]
    histogram = cv2.calcHist([img_hsv], channels, mask, channels_ranges, channels_values) #calculate histogram of H and S channels
    histogram = cv2.normalize(histogram, None, 0, 255, cv2.NORM_MINMAX) #normalize histogram

    dst = cv2.calcBackProject([img_hsv], channels, histogram, channels_values, 1) # calculate back project (find all pixels with color similar to color of face)
    cv2.imshow(title + ' calcBackProject raw result', dst)

    ret, thresholded = cv2.threshold(dst, 25, 255, cv2.THRESH_BINARY) #threshold result of previous step (remove noise etc)
    cv2.imshow(title + ' thresholded', thresholded)

    cv2.waitKey(5000)
    #put partial results into one final image
    row1 = np.hstack((img, cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR), img_masked))
    row2 = np.hstack((img_hsv, cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR), cv2.cvtColor(thresholded, cv2.COLOR_GRAY2BGR)))
    return np.vstack((row1, row2))


paths = ['1.jpg', '2.jpg', '3.jpg', '4.jpg']
haar_cascade = cv2.CascadeClassifier('C:\\DevTools\\src\\opencv\\data\\haarcascades\\haarcascade_frontalface_default.xml') #change it to path to face cascade - it's inside opencv folder

for path in paths:
    img = cv2.imread(path)
    face_pos = haar_cascade.detectMultiScale(img, 1.3, 5, cv2.CASCADE_FIND_BIGGEST_OBJECT)
    if len(face_pos) == 0: #if haar cascade failed to find any face, try again with different (more accurate, but slower) settings
        face_pos = haar_cascade.detectMultiScale(img, 1.1, 3, cv2.CASCADE_FIND_BIGGEST_OBJECT)
    result = process_image(img, face_pos, path)
    cv2.imwrite('result_' + path, result) #save the result

检查图案(规定,简单,别的?)并确定衬衫的颜色
在这里,我会尝试从图像中提取(掩盖)衬衫,而不是只在它上面操作。为了实现它,我将尝试使用与前一部分类似的方法 - 抓取算法。这次初始化可能会更难。我想到的非常简单(但可能不完美)的解决方案是:

  • 在几乎整个区域周围设置矩形(每边只留几个像素)
  • 将遮罩初始化为图片中间的sure foreground值 - 只需使用sure foregroung"颜色"
  • 在中间绘制一些圆圈
  • 将遮罩设置为图像角落的sure background
  • 将面具设置为面部矩形中的sure background(在步骤&#34中使用Haar级联创建的面具;检查图像是否显示具有长袖或短袖的衬衫")

或者,您可以将整个蒙版初始化为sure foregroundpossible foreground,并使用分水岭算法查找大的白色区域(背景为蓝图)。一旦你有这个区域 - 用它作为背景 最有可能同时使用这两种解决方案将为您带来最佳效果。

您也可以尝试更轻松的解决方案。看起来所有的图像都是衬衫,而不是背景,皮肤或其他任何东西都在它的中心。就像这里一样: enter image description here 所以你只能分析这部分衬衫。您可以尝试使用Haar级联来定位这个可靠的衬衫部分图像 - 只需找到面部,然后向下移动已建立的矩形。

一旦你戴上蒙面衬衫,你就可以计算出它的参数。我会尝试的两件事是:

  • 将其转换为HSV颜色空间并计算Hue和Saturations通道的直方图(2个分离 - 不像我们在上一步中所做的那样)。比较2件衬衫的直方图应该有机会找到颜色相近的衬衫。为了比较直方图,我将使用一些(标准化的)相关系数。
  • 使用傅立叶变换来查看这件衬衫中最常见的频率。对于普通衬衫,它应该比剥离的频率小得多。

我知道那些解决方案并不完美,但希望它有所帮助。如果您有任何问题或疑问 - 请随时提出。

<强> //编辑:
我使用傅里叶变换进行了一些简单的模式比较。结果......不是很好,也不是很糟糕 - 总比没有好,但绝对不是完美的;)我想说这是一个很好的起点。
带有代码和图片的打包(你的+来自谷歌的一些)是here。代码:

import cv2
import numpy as np
from collections import OrderedDict
import operator


def shirt_fft(img, face_pos, title):
    shirt_rect_pos = face_pos[0]
    # print shirt_rect_pos
    shirt_rect_pos[1] += 2*shirt_rect_pos[3] #move down (by 2 * its height) rectangle with face - now it will point shirt sample
    shirt_sample = img[shirt_rect_pos[1]:shirt_rect_pos[1]+shirt_rect_pos[3], shirt_rect_pos[0]:shirt_rect_pos[0]+shirt_rect_pos[2]].copy() #crop shirt sample from image
    shirt_sample = cv2.resize(shirt_sample, dsize=(256, 256)) #resize sample to (256,256)
    # cv2.imshow(title+' shirt sample', shirt_sample)

    shirt_sample_gray = cv2.cvtColor(shirt_sample, cv2.COLOR_BGR2GRAY) #convert to gray colorspace

    f = np.fft.fft2(shirt_sample_gray) #calculate fft
    fshift = np.fft.fftshift(f) #shift - now the brightest poitn will be in the middle
    # fshift = fshift.astype(np.float32)
    magnitude_spectrum = 20*np.log(np.abs(fshift)) # calculate magnitude spectrum (it's easier to show)
    print magnitude_spectrum.max(), magnitude_spectrum.min(), magnitude_spectrum.mean(), magnitude_spectrum.dtype
    magnitude_spectrum = cv2.normalize(magnitude_spectrum, alpha=255.0, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8UC1) #normalize the result and convert to 8uc1 (1 channe with 8 bits - unsigned char) datatype
    print magnitude_spectrum.max(), magnitude_spectrum.min(), magnitude_spectrum.mean(), magnitude_spectrum.dtype
    # cv2.imshow(title+' fft magnitude', magnitude_spectrum)
    magnitude_spectrum_original = magnitude_spectrum.copy()
    # temp, magnitude_spectrum = cv2.threshold(magnitude_spectrum, magnitude_spectrum.max()*0.75, 255.0, cv2.THRESH_TOZERO)
    # temp, magnitude_spectrum = cv2.threshold(magnitude_spectrum, 125, 255.0, cv2.THRESH_TOZERO)
    # temp, magnitude_spectrum = cv2.threshold(magnitude_spectrum, 250, 255.0, cv2.THRESH_TOZERO_INV) #clear the brightest part
    temp, magnitude_spectrum = cv2.threshold(magnitude_spectrum, 200, 255.0, cv2.THRESH_TOZERO) #clear all values from 0 to 200 - removes noise etc
    # cv2.imshow(title+' fft magnitude thresholded', magnitude_spectrum)
    # cv2.waitKey(1)

    # if chr(cv2.waitKey(5000)) == 'q':
        # quit()

    # return fshift
    return shirt_sample_gray, magnitude_spectrum_original, magnitude_spectrum

paths = ['1.jpg', '2.jpg', '3.jpg', '4.jpg', 'plain1.jpg', 'plain2.jpg', 'plain3.jpg', 'plain4.jpg', 'stripes1.jpg', 'stripes2.jpg']
haar_cascade = cv2.CascadeClassifier('C:\\DevTools\\src\\opencv\\data\\haarcascades\\haarcascade_frontalface_default.xml') #change it to path to face cascade - it's inside opencv folder

fft_dict = OrderedDict()
results_img = None

for path in paths:
    img = cv2.imread(path)
    face_pos = haar_cascade.detectMultiScale(img, 1.3, 5, cv2.CASCADE_FIND_BIGGEST_OBJECT)
    if len(face_pos) == 0: #if haar cascade failed to find any face, try again with different (more accurate, but slower) settings
        face_pos = haar_cascade.detectMultiScale(img, 1.1, 3, cv2.CASCADE_FIND_BIGGEST_OBJECT)
    # result = process_image(img, face_pos, path)
    # cv2.imwrite('result_' + path, result) #save the result
    results = shirt_fft(img, face_pos, path)
    if results_img is None:
        results_img = np.hstack(results)
    else:
        results_img = np.vstack((results_img, np.hstack(results)))
    fft_dict[path] = results[2]

similarity_dict = {}
cv2.imshow('results_img', results_img)
cv2.waitKey(1)


#for each image calcualte value of correlation with each other image
for i in range(len(fft_dict.keys())):
    for j in range(i+1, len(fft_dict.keys())):
    # for j in range(i, len(fft_dict.keys())):
        key1, key2 = fft_dict.keys()[i], fft_dict.keys()[j]
        print 'pair: ', key1, key2 
        img1 = fft_dict[key1]
        img2 = fft_dict[key2].copy()
        # img2 = img2[10:246, 10:246]
        correlation = cv2.matchTemplate(img1, img2, cv2.TM_CCORR_NORMED)
        # correlation = cv2.matchTemplate(img1, img2, cv2.TM_SQDIFF_NORMED)
        # print correlation
        print correlation.shape, correlation.dtype, correlation.max()
        similarity_dict[key1 + ' - ' + key2] = correlation.max()
        # similarity_dict[key1 + ' - ' + key2] = correlation

#sort values (from best to worst matches)
sorted_similarity_dict = sorted(similarity_dict.items(), key=operator.itemgetter(1), reverse=True)
print "final result: "
for a in sorted_similarity_dict:
    print a


cv2.waitKey(50000)

有些行被评论 - 您可以尝试使用它们,也许您会获得更好的结果 基本算法非常简单 - 对于每个图像:

  • 从图像中剪下衬衫样品(只需将面朝下移动2 *高度的矩形)
  • 将此rect转换为灰色空间并调整为(256,256)
  • 计算此样本的fft
  • 计算fft变换的微米光谱
  • 将其标准化(从0到255)
  • 阈值(清除所有值&lt; 200) - 这将消除噪音等。

现在我们可以计算所有衬衫样本之间该图像的标准化交叉核心。结果很高 - &gt;类似的样本。最终结果:

('plain1.jpg - plain3.jpg', 1.0)  
('plain3.jpg - plain4.jpg', 1.0)  
('plain1.jpg - plain4.jpg', 1.0)  
('stripes1.jpg - stripes2.jpg', 0.54650664)  
('1.jpg - 3.jpg', 0.52512592)  
('plain1.jpg - stripes1.jpg', 0.45395589)  
('plain3.jpg - stripes1.jpg', 0.45395589)  
('plain4.jpg - stripes1.jpg', 0.45395589)  
('plain1.jpg - plain2.jpg', 0.39764369)  
('plain2.jpg - plain4.jpg', 0.39764369)  
('plain2.jpg - plain3.jpg', 0.39764369)  
('2.jpg - stripes1.jpg', 0.36927304)  
('2.jpg - plain3.jpg', 0.35678366)  
('2.jpg - plain4.jpg', 0.35678366)  
('2.jpg - plain1.jpg', 0.35678366)  
('1.jpg - plain1.jpg', 0.28958824)  
('1.jpg - plain3.jpg', 0.28958824)  
('1.jpg - plain4.jpg', 0.28958824)  
('2.jpg - 3.jpg', 0.27775836)  
('4.jpg - plain3.jpg', 0.2560707)  
('4.jpg - plain1.jpg', 0.2560707)  
('4.jpg - plain4.jpg', 0.2560707)  
('3.jpg - stripes1.jpg', 0.25498456)  
('4.jpg - plain2.jpg', 0.24522379)  
('1.jpg - 2.jpg', 0.2445447)  
('plain4.jpg - stripes2.jpg', 0.24032137)  
('plain3.jpg - stripes2.jpg', 0.24032137)  
('plain1.jpg - stripes2.jpg', 0.24032137)  
('3.jpg - stripes2.jpg', 0.23217434)  
('plain2.jpg - stripes2.jpg', 0.22518013)  
('2.jpg - stripes2.jpg', 0.19549081)  
('plain2.jpg - stripes1.jpg', 0.1805127)  
('3.jpg - plain4.jpg', 0.14908621)  
('3.jpg - plain1.jpg', 0.14908621)  
('3.jpg - plain3.jpg', 0.14908621)  
('4.jpg - stripes2.jpg', 0.14738286)  
('2.jpg - plain2.jpg', 0.14187276)  
('3.jpg - 4.jpg', 0.13638313)  
('1.jpg - stripes1.jpg', 0.13146029)  
('4.jpg - stripes1.jpg', 0.11624481)  
('1.jpg - plain2.jpg', 0.11515292)  
('2.jpg - 4.jpg', 0.091361843)  
('1.jpg - 4.jpg', 0.074155055)  
('1.jpg - stripes2.jpg', 0.069594234)  
('3.jpg - plain2.jpg', 0.059283193)  

包含所有衬衫样本,幅度谱(阈值前后)的图像在这里: enter image description here

图像名称(与此大图像上的样本的顺序相同):['1.jpg', '2.jpg', '3.jpg', '4.jpg', 'plain1.jpg', 'plain2.jpg', 'plain3.jpg', 'plain4.jpg', 'stripes1.jpg', 'stripes2.jpg'] 如您所见,对于具有相同模式的样本,阈值图像非常相似。我认为如果你找到一种更好的方法来比较这些图像(阈值幅度谱),这个解决方案可以更好地工作。

<强> EDIT2:
只是一个简单的想法 - 在你从很多衬衫裁剪衬衫样品后,你可以尝试训练一些分类器,然后使用这个分类器识别它们的模式。寻找有关训练Haar或LBP(局部二进制模式)级联的教程。