Files @ 92f4748d07b3
Branch filter:

Location: OneEye/exp/stone_detect.py - annotation

Laman
exp: connecting segmented stones into lines vol 2
8be76da66456
8be76da66456
8be76da66456
855e8825c380
8be76da66456
92f4748d07b3
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
8be76da66456
8be76da66456
92f4748d07b3
92f4748d07b3
8be76da66456
8be76da66456
8be76da66456
8be76da66456
8be76da66456
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
8be76da66456
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
92f4748d07b3
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
8be76da66456
855e8825c380
8be76da66456
8be76da66456
855e8825c380
855e8825c380
855e8825c380
855e8825c380
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
8be76da66456
8be76da66456
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
8be76da66456
8be76da66456
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
92f4748d07b3
855e8825c380
92f4748d07b3
92f4748d07b3
855e8825c380
92f4748d07b3
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
855e8825c380
8be76da66456
8be76da66456
8be76da66456
8be76da66456
92f4748d07b3
8be76da66456
8be76da66456
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
92f4748d07b3
8be76da66456
import sys
sys.path.append("../src")

import os
import math
import random

import cv2 as cv
import numpy as np
import scipy.cluster
import scipy.ndimage

from annotations import DataFile,computeBoundingBox
from hough import show
from analyzer.epoint import EPoint

random.seed(361)


class NeighboringPoint(EPoint):
	def __init__(self,x,y):
		super().__init__(x,y)
		self.neighbours=[]


def kmeans(img):
	arr=np.reshape(img,(-1,3)).astype(np.float)
	colors=np.array([[0,0,0],[255,255,255],[193,165,116]],np.float)
	print(colors)
	(centers,distortion)=scipy.cluster.vq.kmeans(arr,colors)
	print("k-means centers:",centers)
	return centers


def quantize(img,centers):
	origShape=img.shape
	data=np.reshape(img,(-1,3))
	(keys,dists)=scipy.cluster.vq.vq(data,centers)
	pixels=np.array([centers[k] for k in keys],dtype=np.uint8).reshape(origShape)
	return pixels


def filterStones(contours,bwImg,stoneDims):
	contourImg=cv.cvtColor(bwImg,cv.COLOR_GRAY2BGR)
	res=[]
	for (i,c) in enumerate(contours):
		keep=True
		moments=cv.moments(c)
		center=(moments["m10"]/(moments["m00"] or 1), moments["m01"]/(moments["m00"] or 1))
		area=cv.contourArea(c)
		(x,y,w,h)=cv.boundingRect(c)
		if w>stoneDims[0] or h>stoneDims[1]*1.5 or w<2 or h<2:
			cv.drawMarker(contourImg,tuple(map(int,center)),(0,0,255),cv.MARKER_TILTED_CROSS,12)
			keep=False
		coverage1=area/(w*h or 1)
		hull=cv.convexHull(c)
		coverage2=area/(cv.contourArea(hull) or 1)
		if coverage2<0.8:
			cv.drawMarker(contourImg,tuple(map(int,center)),(0,127,255),cv.MARKER_DIAMOND,12)
			keep=False
		if keep:
			res.append((EPoint(*center),c))
			cv.drawMarker(contourImg,tuple(map(int,center)),(255,0,0),cv.MARKER_CROSS)
	print("accepted:",len(res))
	print("rejected:",len(contours)-len(res))
	show(contourImg)
	return res


def point2lineDistance(a,b,p):
	# https://en.wikipedia.org/wiki/Point-line_distance#Line_defined_by_two_points
	ab=b-a
	num=abs(ab.y*p.x - ab.x*p.y + b.x*a.y - a.x*b.y)
	denum=math.sqrt(ab.y**2+ab.x**2)
	return num/denum # double_area / side_length == height


def groupLines(points,minCount,tolerance):
	random.shuffle(points)
	sample=points[:57]
	for (i,a) in enumerate(sample):
		for (j,b) in enumerate(sample):
			if j<=i: continue
			incidentPoints=[a,b]
			for c in points:
				if c is a or c is b: continue
				if point2lineDistance(a,b,c)<=tolerance:
					incidentPoints.append(c)
			if len(incidentPoints)>=minCount:
				yield incidentPoints


if __name__=="__main__":
	filepath=sys.argv[1]
	annotations=DataFile(sys.argv[2])
	filename=os.path.basename(filepath)
	(x1,y1,x2,y2)=computeBoundingBox(annotations[filename][0])
	(w,h)=(x2-x1,y2-y1)
	img=cv.imread(filepath)
	(x3,x4,y3,y4)=(x1+w//4,x1+3*w//4,y1+h//4,y1+3*h//4)
	print("x3,x4,y3,y4:",x3,x4,y3,y4)
	rect=img[y3:y4,x3:x4,:]
	centers=kmeans(rect)
	print("x1,x2,y1,y2:",(x1,x2,y1,y2))
	img[y1:y2,x1:x2,:]=quantize(img[y1:y2,x1:x2,:],centers)
	print("image quantized")

	rect=img[y1:y2,x1:x2]
	unit=np.array([1,1,1],dtype=np.uint8)
	kernel=np.ones((3,3),np.uint8)
	maskB=cv.inRange(rect,centers[0]-unit,centers[0]+unit)
	maskB=cv.morphologyEx(maskB,cv.MORPH_OPEN,kernel,iterations=1)
	maskB=cv.erode(maskB,kernel,iterations=2)
	maskW=cv.inRange(rect,centers[1]-unit,centers[1]+unit)
	maskW=cv.erode(maskW,kernel,iterations=2)

	show(img,filename)
	show(maskB,filename)
	show(maskW,filename)
	stones=cv.bitwise_or(maskB,maskW)
	show(stones)

	stoneDims=(w/19,h/19)
	print("stone dims:",tuple(x/2 for x in stoneDims),"-",stoneDims)

	(contours,hierarchy)=cv.findContours(stones,cv.RETR_LIST,cv.CHAIN_APPROX_SIMPLE)
	stoneLocs=filterStones(contours,stones,stoneDims)

	linesImg=cv.cvtColor(np.zeros((h,w),np.uint8),cv.COLOR_GRAY2BGR)
	cv.drawContours(linesImg,[c for (point,c) in stoneLocs],-1,(255,255,255),-1)
	for (p,c) in stoneLocs:
		cv.drawMarker(linesImg,(int(p.x),int(p.y)),(255,0,0),cv.MARKER_CROSS)

	lineSet=set()
	minCount=min(max(math.sqrt(len(stoneLocs))-2,3),7)
	print("min count:",minCount)
	for points in groupLines([point for (point,contour) in stoneLocs],minCount,2):
		points=tuple(sorted(tuple(p) for p in points))
		lineSet.add(points)
		obsolete=set()
		pointSet=set(points)
		for line in lineSet:
			lineS=set(line)
			if points is line: continue
			if pointSet<lineS:
				lineSet.remove(points)
				break
			if lineS<pointSet:
				obsolete.add(line)
		lineSet-=obsolete
	for line in sorted(lineSet,key=len,reverse=True)[:16]:
		print(len(line),line)
		(xa,ya)=line[0]
		(xb,yb)=line[-1]
		cv.line(linesImg,(int(xa),int(ya)),((int(xb),int(yb))),(255,255,0),1)
	show(linesImg)