|
from PIL import Image, ImageDraw, ImageFont |
|
import sys |
|
from math import sqrt, pow, floor |
|
import random |
|
import numpy |
|
|
|
def normalize(im): |
|
w,h = im.size |
|
outpic = im.copy() |
|
out = outpic.load() |
|
high = 0 |
|
low = 0 |
|
for x in range(w): |
|
for y in range(h): |
|
if out[x,y][0] > high: |
|
high = out[x,y][0] |
|
if out[x,y][0] < low: |
|
low = out[x,y][0] |
|
prop = 256.0/(high - low) |
|
for x in range(w): |
|
for y in range(h): |
|
n = int(floor((out[x,y][0] - low)* prop)) |
|
out[x,y] = (n,n,n) |
|
return outpic |
|
|
|
def binarize(im, thresh): |
|
w,h = im.size |
|
outpic = im.copy() |
|
out = outpic.load() |
|
for x in range(w): |
|
for y in range(h): |
|
if outpic.mode == 'RGB': |
|
if out[x,y][0] >= thresh: |
|
out[x,y] = (255,255,255) |
|
else: |
|
out[x,y] = (0,0,0) |
|
|
|
if x<5 or x>w-5 or y<5 or y>w-5: |
|
out[x,y] = (0,0,0) |
|
|
|
else: |
|
if out[x,y] >= thresh: |
|
out[x,y] = 255 |
|
else: |
|
out[x,y] = 0 |
|
|
|
if x<5 or x>w-5 or y<5 or y>w-5: |
|
out[x,y] = 0 |
|
outpic.save('binarized.png') |
|
return outpic |
|
|
|
def convolution(im): |
|
pix = im.load() |
|
outpic = im.copy() |
|
out = outpic.load() |
|
w,h = im.size |
|
maskx = [[-1.0, 0.0, 1.0], [-1.0, 0.0, 1.0], [-1.0, 0.0, 1.0]] |
|
masky = [[1.0, 1.0, 1.0], [0.0, 0.0, 0.0], [-1.0, -1.0, -1.0]] |
|
values = {} |
|
for x in range(w): |
|
for y in range(h): |
|
gx, gy = (0.0, 0.0) |
|
r,g,b =0,0,0 |
|
for i in range(x-1, x+2): |
|
for j in range(y-1, y+2): |
|
try: |
|
sum_=0 |
|
r = pix[i, j][0] |
|
g = pix[i, j][1] |
|
b = pix[i, j][2] |
|
sum_ = (r+g+b)/3 |
|
gx += sum_ * maskx[i - (x-1)][j - (y-1)] |
|
gy += sum_ * masky[i - (x-1)][j - (y-1)] |
|
except IndexError: |
|
pass |
|
g = int(sqrt(pow(gx, 2) + pow(gy, 2))) |
|
out[x,y] = (g,g,g) |
|
if gx != 0.0: |
|
values[i,j] = gy/gx |
|
else: |
|
values[i,j] = 1000 |
|
return outpic, values |
|
|
|
def bfs(im, root, color): |
|
'''q = queue ''' |
|
pix = im.load() |
|
w, h = im.size |
|
q = [] |
|
q.append(root) |
|
coords = [] |
|
original = pix[root] |
|
tot = 0 |
|
while len(q) > 0: |
|
(x, y) = q.pop(0) |
|
curr = pix[x, y] |
|
if curr == original or curr == color: |
|
for dx in [-1, 0, 1]: |
|
for dy in [-1, 0, 1]: |
|
i, j = (x + dx, y + dy) |
|
if i >= 0 and i < w and j >= 0 and j < h: |
|
rgb = pix[i, j] |
|
if rgb == original: |
|
tot +=1 |
|
pix[i, j] = color |
|
coords.append((i, j)) |
|
q.append((i, j)) |
|
return coords, tot, im |
|
|
|
def preprocessing(im): |
|
im, values = convolution(im) |
|
im = normalize(im) |
|
im = binarize(im,60) |
|
im.save('preprocessing.png') |
|
pix = im.load() |
|
ellipses = [] |
|
w,h = im.size |
|
area = w*h |
|
props = [] |
|
for x in range(w): |
|
for y in range(h): |
|
if pix[x,y] == (255,255,255): |
|
coords, tot, im = bfs(im, (x,y), (255,0,0)) |
|
p = (float(tot)/area) |
|
|
|
if p > 0.01: |
|
props.append(p) |
|
ellipses.append(coords) |
|
return ellipses, values, im, props |
|
|
|
def choosePoints(ellipses): |
|
points = [] |
|
for i in range(len(ellipses)): |
|
borders = len(ellipses[i]) - 1 |
|
for j in range(len(ellipses[i])): |
|
#p1 = random.choice(ellipses[i]) |
|
#p2 = random.choice(ellipses[i]) |
|
p1 = random.randint(0, borders/2) |
|
p1 = ellipses[i][p1] |
|
p2 = random.randint(borders/2, borders) |
|
p2 = ellipses[i][p2] |
|
points.append((p1, p2)) |
|
return points |
|
|
|
def chordTangent(p1, p2, g1, g2, im, gray): |
|
original = im.copy() |
|
draw = ImageDraw.Draw(im) |
|
draw.ellipse((p1[0]-1,p1[1]-1, p1[0]+1,p1[1]+1), fill='red') |
|
draw.ellipse((p2[0]-1,p2[1]-1, p2[0]+1,p2[1]+1), fill='red') |
|
|
|
#tangents |
|
t1 = [g1, p1[1] - (g1*p1[0])] |
|
t2 = [g2, p2[1] - (g2*p2[0])] |
|
tangent = (0, (t1[0]*0 + t1[1]) ,128, (t1[0]*128 + t1[1])) |
|
draw.line(tangent, fill='yellow') |
|
tangent = (0, (t2[0]*0 + t2[1]) ,128, (t2[0]*128 + t2[1])) |
|
draw.line(tangent, fill='yellow') |
|
|
|
#intersection |
|
x = float(t2[1] - t1[1]) / (t1[0] - t2[0]) |
|
y = t1[0] * x + t1[1] |
|
|
|
#midpoint ym=(point1_y+point2_y)/2, xm=(point1_x+point2_x)/2 |
|
mdpoint = ((p1[0]+p2[0])/2, (p1[1]+p2[1])/2) |
|
|
|
#line between midpoint and intersection. m: slope y = mx + b |
|
if (x - mdpoint[0]) != 0: |
|
m = (y - mdpoint[1] ) / (x - mdpoint[0]) |
|
else: |
|
m = 1000 |
|
|
|
b = mdpoint[1] - (m * mdpoint[0]) |
|
|
|
line = (mdpoint[0],mdpoint[1], x,y) |
|
draw.line(line, fill='red') |
|
draw.ellipse((mdpoint[0]-2, mdpoint[1]-2, mdpoint[0]+2, mdpoint[1]+2), fill='yellow') |
|
if (x - mdpoint[0]) < 0.0: |
|
direction = 1 |
|
else: |
|
direction = -1 |
|
gray = posibleCenter(m, b, mdpoint[0], direction, gray) |
|
return gray |
|
|
|
def posibleCenter(m, b, start, direction, im): |
|
x = start |
|
w,h = im.size |
|
pix = im.load() |
|
while x >= 0 and x < w: |
|
y = int((m * x) + b) |
|
if y >= 0 and y < h: |
|
pix[x,y] += 5 |
|
else: |
|
break |
|
x += direction |
|
return im |
|
|
|
def center(im): |
|
im = im.convert('RGB') |
|
ellipses = [] |
|
centers = [] |
|
pix = im.load() |
|
w,h = im.size |
|
for x in range(w): |
|
for y in range(h): |
|
if pix[x,y] == (255,255,255): |
|
coords, tot, im = bfs(im, (x,y), (0,0,255)) |
|
if len(coords) > 2: |
|
ellipses.append(coords) |
|
for e in ellipses: |
|
x, y = 0 , 0 |
|
for p in e: |
|
x += p[0] |
|
y += p[1] |
|
x = int(float(x)/len(e)) |
|
y = int(float(y)/len(e)) |
|
c = (x,y) |
|
centers.append(c) |
|
return centers |
|
|
|
def orange(): |
|
return (random.randint(235,255), random.randint(0,150), random.randint(0, 50)) |
|
|
|
def ellipseDetection(image): |
|
original = image.copy() |
|
image.thumbnail((128,128), Image.ANTIALIAS) |
|
ellipses, values, pre, props = preprocessing(image) |
|
points = choosePoints(ellipses) |
|
gray = Image.new('L', image.size, (0)) |
|
for point in points: |
|
if values[point[0]] != values[point[1]]: |
|
gray = chordTangent(point[0], point[1], values[point[0]], values[point[1]], image, gray) |
|
gray.save('centers.png') |
|
im = binarize(gray, 90) |
|
centers = center(im) |
|
pre.save('pre.png') |
|
pix = pre.load() |
|
w, h = image.size |
|
w = float(original.size[0]) / w |
|
h = float(original.size[1]) / h |
|
dim = [] |
|
prop = 5 |
|
for c in centers: |
|
curr = list(c[:]) |
|
horizontal = 0 |
|
while pix[tuple(curr)] != (255, 0, 0): |
|
curr[0] += 1 |
|
horizontal += 1 |
|
if horizontal > 128: |
|
break |
|
|
|
curr = list(c[:]) |
|
vertical = 0 |
|
while pix[tuple(curr)] != (255, 0, 0): |
|
curr[1] += 1 |
|
vertical += 1 |
|
if vertical > 128: |
|
break |
|
dim.append((horizontal, vertical)) |
|
|
|
draw = ImageDraw.Draw(original) |
|
font = ImageFont.truetype("/usr/share/fonts/truetype/freefont/FreeMonoOblique.ttf", 18) |
|
|
|
for i in range(len(centers)): |
|
id_ = 'e'+str(i+1) |
|
p = props[i] * 100 |
|
print 'Elipse %d: %.2f%%' %(i+1, p) |
|
x = int(centers[i][0] * w) |
|
y = int(centers[i][1] * h) |
|
draw.text((x, y + (prop*2)), id_, fill='red', font=font) |
|
draw.ellipse((x-dim[i][0] * w, y-dim[i][1] * h, x+dim[i][0] * w, y + dim[i][1] * h), outline = orange()) |
|
draw.ellipse((x-prop, y-prop, x+prop, y+prop), fill='green') |
|
original.save('detection.png') |
|
if __name__ == '__main__': |
|
path = sys.argv[1] |
|
image = Image.open(path) |
|
ellipseDetection(image) |
"labratorio". Hubiera sido bueno diferenciar entre círculos y otros elipses y comprobar que no agarre otras formas. No se me hacen rellenados tampoco en tu ejemplo. 7 pts.
ReplyDelete