比較している画像を見ると、実際にはやなどの指標を使用したくありませんRMSE
。その理由は、すべての画像が「RMSEセンス」で類似しているため、または画像に存在する基本的な関係を考慮しない、より洗練されたメトリックの場合でさえあるためです。これがあなた自身によって与えられたいくつかの例です:

あなたの場合の基本的な関係は次のとおりです:色(スペード、ハート、ダイヤ、クラブカードも区別します)、および形状の測定。したがって、カードの色を検出することにより、検索スペースが削減され、残っているのはカードの上部にある数字を識別することだけです。接続されたコンポーネントの数とオイラー数とともに、検索はさらに制限されます。残っているのは、「9」と「6」、「4」、クイーン、または「A」の区別です。「J」、「2」、「5」、または「7」からの「3」。「8」と「10」が解決されます。前者はオイラー数によるもので、後者は連結成分の数によるものです(これはすべてカードが一意であると想定しています。そうでない場合は、先に進んで最も類似したカードを見つけます)。ここで行う最も簡単なことは、
これは、これらの点を考慮し、指定されたすべての入力に対して機能する単純な実装です。比較する画像を探すための画像とディレクトリが必要です。各ステップを改善することができます。
import sys
import numpy
from scipy.ndimage import morphology, label, find_objects
from PIL import Image
COLORS = range(4)
RED, GREEN, BLUE, BLACK = COLORS
def card_color(img):
im = img.load()
width, height = img.size
black, blue, green, red = 0, 0, 0, 0
for x in xrange(width):
for y in xrange(height):
r, g, b = im[x, y]
if r > 200 and g > 200 and b > 200:
# "white", ignore
continue
if r > 200 and g < 100 and b < 100:
red += 1
elif r < 100 and g < 100 and b > 200:
blue += 1
elif r < 50 and g < 50 and b < 50:
black += 1
elif r < 100 and g > 120 and b < 50: # dark green
green += 1
return max(zip((black, blue, green, red), COLORS))
def euler_number(img, conn=4):
im = img.load()
width, height = img.size
c1, c2, c3 = 0, 0, 0
for x in xrange(width - 1):
for y in xrange(height - 1):
s = (im[x,y] + im[x+1,y] + im[x,y+1] + im[x+1,y+1]) / 255
if s == 1:
c1 += 1
elif s == 2:
if (im[x+1,y] and im[x,y+1]) or (im[x,y] and im[x+1,y+1]):
c3 += 1
elif s == 3:
c2 += 1
if conn == 4:
return (c1 - c2 + 2 * c3) / 4
else: # 8
return (c1 - c2 - 2 * c3) / 4
def carefully_binarize(img, color):
if color == BLACK:
img = img.convert('L')
else:
img = img.split()[color]
width, height = img.size
im = numpy.empty((height + 2, width + 2), dtype=numpy.uint8) # Padding
im.fill(255)
im[1:-1, 1:-1] = numpy.array(img)
threshold = im.mean() - im.std()
im[im <= threshold] = 1
im[im > threshold] = 0
# Discard small components.
lbl, ncc = label(im)
for i in xrange(1, ncc + 1):
py, px = numpy.nonzero(lbl == i)
if len(py) < 30:
im[lbl == i] = 0
return Image.fromarray(im * 255)
def discard_bottom(img, k=0.5):
width, height = img.size
im = numpy.array(img)
limit = height * k
lbl, ncc = label(im)
for i, oslice in enumerate(find_objects(lbl)):
srow, scol = oslice
if srow.stop > limit:
ncc -= 1
im[srow.start:srow.stop, scol.start:scol.stop] = 0
return Image.fromarray(im), ncc
def signature(img):
# Assumption: a single connected component is present now.
im = numpy.array(img)
im = morphology.binary_fill_holes(im)
im = morphology.binary_dilation(im) - im
py, px = numpy.nonzero(im)
return Image.fromarray(im.astype(numpy.uint8)*255), zip(py, px)
def hausdorff(a, b):
dist = 0
for ai in a:
mindist = float('inf')
for bi in b:
chess = max(abs(ai[0]-bi[0]), abs(ai[1]-bi[1]))
if chess < mindist:
mindist = chess
if mindist > dist:
dist = mindist
return dist
img1 = Image.open(sys.argv[1]).convert('RGB')
dirpath = sys.argv[2]
img1_color = card_color(img1)[1]
img1 = carefully_binarize(img1, img1_color)
img1_top, img1_top_ncc = discard_bottom(img1)
img1_top_en = euler_number(img1_top)
feature = [img1_color, img1_top_ncc, img1_top_en]
match = []
for fname in os.listdir(dirpath):
try:
img2 = Image.open(os.path.join(dirpath, fname)).convert('RGB')
except IOError:
print "Ignoring", fname
continue
if card_color(img2)[1] != feature[0]:
continue
img2 = carefully_binarize(img2, feature[0])
img2_top, ncc = discard_bottom(img2)
if ncc != feature[1]:
continue
en = euler_number(img2_top)
if en != feature[2]:
continue
match.append((img2_top, os.path.join(dirpath, fname)))
if len(match) == 1:
print "Here is your best match:", match[0][1]
else:
img1_sig, sig1 = signature(img1_top)
best_match = float('inf'), None
for img2, fname in match:
img2_sig, sig2 = signature(img2)
dist = hausdorff(sig1, sig2)
if dist < best_match[0]:
best_match = dist, fname
print "Best match:", best_match[1]