Skip to content

Instantly share code, notes, and snippets.

@bootjp
Forked from ksasao/detect_marker.py
Created April 30, 2018 12:43
Show Gist options
  • Save bootjp/737e219463116f60de829ff250e01839 to your computer and use it in GitHub Desktop.
Save bootjp/737e219463116f60de829ff250e01839 to your computer and use it in GitHub Desktop.
ZOZOSUITのマーカーのIDを読み取るコードです。公開されている画像を元に独自に解析しているので、公式ではこのように処理しているかどうかは不明です。
import numpy as np
import random
import math
import cv2
from PIL import Image
def detect_markers(im):
markers = []
# 輪郭線抽出のための二値化
im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
im_blur = cv2.GaussianBlur(im_gray, (3, 3), 0)
ret,th = cv2.threshold(im_blur, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# 画像から輪郭線を抽出
imgEdge,contours,hierarchy = cv2.findContours(th, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
# 背景を黒に
im[:] = (255, 255, 255)
for (i,cnt) in enumerate(contours):
# 輪郭線領域を長方形に近似する
rect = cv2.minAreaRect(cnt)
s = rect[1][0] * rect[1][1] # 面積
p = rect[1][0] / (rect[1][1] + 0.01) # 縦横比
if s > 8000 : continue
if p < 0.9 or p > 1.1 : continue
# 楕円で近似する
if (cnt.shape)[0]>4:
ellipse = cv2.fitEllipse(cnt)
cx=int(ellipse[0][0])
cy=int(ellipse[0][1])
# 楕円の内部に点があれば楕円を描画
con = hierarchy[0][i][2]
if con != -1:
cv2.ellipse(im,ellipse,(53,130,84),2)
else: continue
# 楕円の内側の点を列挙
points = []
while con != -1:
x,y,w,h = cv2.boundingRect(contours[con])
xx = int(x + w/2)
yy = int(y + h/2)
a = np.array([cx, cy])
b = np.array([xx, yy])
dest = np.linalg.norm(b-a)
points.append([xx,yy,dest])
con = hierarchy[0][con][0]
# 最も楕円の中心に近い点
xc,yc,dc = min(points, key=(lambda x: x[2]))
# 楕円の中心に近い点を原点として周囲の点との距離を再計算
plist = []
for xx,yy,dd in points:
a = np.array([xc, yc])
b = np.array([xx, yy])
dest = np.linalg.norm(b-a)
plist.append([xx,yy,dest])
# 外周部と中間部を分離
_,_,dmx = max(points, key=(lambda x: x[2]))
ilist = []
olist = []
for xx,yy,dd in plist:
if dd > 0:
if 1.0 * dd / dmx > 0.7:
olist.append([xx,yy,dd])
cv2.line(im, (xx - 1,yy - 1),(xx + 1, yy + 1), (0,0,255), 2)
cv2.line(im, (xx + 1,yy - 1),(xx - 1, yy + 1), (0,0,255), 2)
else:
ilist.append([xx,yy,dd])
cv2.line(im, (xx - 1,yy - 1),(xx + 1, yy + 1), (143,82,47), 2)
cv2.line(im, (xx + 1,yy - 1),(xx - 1, yy + 1), (143,82,47), 2)
# 中心を描画
cv2.line(im, (xc - 1,yc - 1),(xc + 1, yc + 1), (0,192,255), 2)
cv2.line(im, (xc + 1,yc - 1),(xc - 1, yc + 1), (0,192,255), 2)
# マーカーとして正しく読めているか判定
if len(ilist) >= 2 and len(olist) >= 2:
cv2.circle(im, (xc, yc), 30 , (255,0,0))
id = get_id(im, (xc,yc), ilist, olist)
markers.append([id,xc,yc,dmx])
font = cv2.FONT_HERSHEY_PLAIN
text = format(id, 'x')
cv2.putText(im,text,(cx, cy-30),font, 2,(0,0,255))
return markers
def get_id(im, center, inner, outer):
# 外周部のうち一番左側にある点を基準としてIDを計算する
x,y,d = min(outer, key=(lambda x: x[0]))
cx, cy = center
vx, vy = x - cx, y - cy
bito, biti = 0, 0
# 60度ずつ回転してそこに点があるかどうかを確認する
for i in range(0,6):
bito = bito << 1
biti = biti << 1
# 外側の点をビット列に
rado = i * math.pi / 3.0
ox = math.cos(rado) * vx + math.sin(rado) * vy + cx
oy = -math.sin(rado) * vx + math.cos(rado) * vy + cy
oa = np.array([ox, oy])
for xx,yy,_ in outer:
b = np.array([xx, yy])
dd = np.linalg.norm(b-oa)
if dd < d / 4:
bito = bito + 1
cv2.circle(im, (int(ox), int(oy)), 3 , (0,0,255))
continue
# 内側の点をビット列に
radi = (i + 0.5) * math.pi / 3.0
ix = math.cos(radi) * vx / 2.0 + math.sin(radi) * vy / 2.0 + cx
iy = -math.sin(radi) * vx / 2.0 + math.cos(radi) * vy / 2.0 + cy
ia = np.array([ix, iy])
for xx,yy,_ in inner:
b = np.array([xx, yy])
dd = np.linalg.norm(b-ia)
if dd < d / 4:
biti = biti + 1
cv2.circle(im, (int(ix), int(iy)), 3 , (0,0,255))
continue
# id を正規化(ビットをローテートしたときの最大値)
idlist = []
for i in range(0,6):
id = (bito << 6) | biti
idlist.append(id)
bito = bito << 1
if bito & 0x40 > 0:
bito = bito | 1
bito = bito & 0x3F
biti = biti << 1
if biti & 0x40 > 0:
biti = biti | 1
biti = biti & 0x3F
return max(idlist)
if __name__ == '__main__':
input_file_name = "input.jpg"
output_file_name = "output.png"
frame = cv2.imread(input_file_name)
id_list = detect_markers(frame)
for id in id_list:
print(id)
cv2.imshow('window',frame)
cv2.imwrite(output_file_name, frame)
key = cv2.waitKey(3000)
cv2.destroyAllWindows()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment