-
-
Save ikedaosushi/3673bcad307292cc80a0183ed9b8396f to your computer and use it in GitHub Desktop.
ZOZOSUITのマーカーのIDを読み取るコードです。公開されている画像を元に独自に解析しているので、公式ではこのように処理しているかどうかは不明です。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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