Du må være registrert og logget inn for å kunne legge ut innlegg på freak.no
X
LOGG INN
... eller du kan registrere deg nå
Dette nettstedet er avhengig av annonseinntekter for å holde driften og videre utvikling igang. Vi liker ikke reklame heller, men alternativene er ikke mange. Vær snill å vurder å slå av annonseblokkering, eller å abonnere på en reklamefri utgave av nettstedet.
  10 3089
Jeg tipper at dette er sykt vanskelig å gjøre, men jeg prøver allikevel. Nedenfor har jeg lagt ved tre eksempelbilder, det jeg ønsker å gjøre er å ha et script som kan lokalisere den hvite pilen og identifisere retningen til pilen. Pilen vil alltid ha samme størrelse, men posisjonen og rotasjonen vil forandre seg. Som dere sikkert ser så har bakgrunnen et pattern, noe som gjør denne oppgaven enda vanskeligere.



Jeg vet at det finnes libraries man kan bruke for å sammenligne bilder, f.eks. OpenCV, men jeg vet ikke om det finnes libraries for å bistå med det jeg ønsker å gjøre? Jeg vet at dette er en typisk ting som er utrolig lett for et menneske å se, men utrolig vanskelig å få et program til å forstå.

Jeg ønsker altså å gjøre dette med Python, er det noe håp? Eller blir dette veldig vanskelig å få til?
Sist endret av stepry; 4. september 2016 kl. 17:03.
Er pilen alltid i så stor kontrast til bakgrunnen? I så fall holder det rett og slett å finne pilen ved å se på pixelfarger. Du kan først fjerne bakgrunnen ved å trekke fra gjennomsnittsfargen fra hver pixel, og se på det hele i gråtoner. Da sitter du igjen med et 2D-array med kun pilen igjen. Å finne retningen til pilen kan gjøres på flere måter, men enklest er nok å avgjøre om den peker opp/ned og venstre/høyre, så slå opp noen kjente verdier fra en tabell. Det spørs litt hvor raskt dette skal gjøres, hvor stort bildet er, og hvilke variasjoner du kan støte på.
Jeg vet at det finnes libraries man kan bruke for å sammenligne bilder, f.eks. OpenCV, men jeg vet ikke om det finnes libraries for å bistå med det jeg ønsker å gjøre?
Vis hele sitatet...
Du kan se på denne koden som jeg skrev for en del år siden.
Det er mulig og lære opp TrainingTesseract til og finne symboler editor.
OpenCv se på Finding contours.

Det skjer også mye i Python sin vitenskapelig stack,f.eks Scikit-image noe som kan passe kan være Template Matching.
Dette problemet er nok kanskje ikke riktig så vanskelig å løse som det virker som. Jeg kommer i hvert fall på to ganske enkle løsninger. (Det skal sies at navnene på bildene dine angir en rotasjon jeg ikke helt ser at er konsekvent med noen akse. Så jeg løser denne vinkelen med hensyn på horisontalaksen. Det kan også nevnes at vertikalaksen når vi ser på bildet normalt tenkes på som positiv opp, mens pixlene defineres fra topp og ned, noe som må taes hensyn til i koden.)

Den minst elegante først: Kort og godt roterer du bildet suksessivt, og plukker ut pixlene til pilen ved å se på hvor lys gråfargen er. Deretter lagrer du unna hvor mange lyse pixler du har på linje i x-retning – altså som har samme y-verdi. Når pilen er tilnærmet horisontal, vil denne verdien være størst. For å finne ut om pilen peker mot venstre eller høyre, kan du finne midtpunktet på pilen og se hvilken side av midtpunktet som har flest lyse pixler. Om du ønsker å finne posisjonen til pilspissen, kan du ganske enkelt gjøre det med å plukke ut pilspisskoordinatene fra det roterte bildet (den lyse pixelen lengst til høyre eller venstre, ettersom orienteringen du fant), og regne ut ny (altså opprinnelig) posisjon fra rotasjonsvinkelen du allerede har. Du kan optimalisere med å beskjære bildet rundt pilen, så fremt du er villig til å gjøre et ekstra regneledd om du trenger posisjonen. Her vil det være en avveining av behovet for nøyaktighet og effektivitet: Desto mer finoppløst du gjør roteringen, jo høyere nøyaktighet vil du få på bekostning av prosesseringstid – men se også kommentaren om nøyaktighet nederst i innlegget. Her er et fungerende eksempel jeg skrev uten en slik beskjæringsoptimalisering, og som heller ikke regner tilbake til posisjonen:

Kode

#!/usr/bin/env python

import sys
from PIL import Image
from collections import Counter

def indexes_to_coords(indexes, size):
    coords = [(index % size[0], int(index / size[1])) for index in indexes]
    return coords


def find_light_pixel_indexes(pixels):
    indexes = []
    for i, pixel in enumerate(pixels):
        if pixel[0] > 200:
            indexes.append(i)
    return indexes


def find_num_y_aligned_points(points):
    y_coords = [p[1] for p in points]
    count = Counter(y_coords)
    return max(count.values())


def is_pointing_left(points):
    x_positions = [c[0] for c in points]
    min_x_pos = min(x_positions)
    max_x_pos = max(x_positions)
    x_midpoint = (min_x_pos + max_x_pos) / 2
    n_points_sub_midpoint = len([x for x in x_positions if x < x_midpoint])
    return n_points_sub_midpoint > len(x_positions) / 2

    
def main(image_file):
    image = Image.open(image_file)
    image.load()

    max_aligned_points = 0
    for angle in range(0, 360, 5):
        rotated_image = image.rotate(360 - angle, expand = 1)
        pixels = rotated_image.getdata()
        arrow_pixel_indexes = find_light_pixel_indexes(pixels)
        arrow_pixel_coords = indexes_to_coords(arrow_pixel_indexes, rotated_image.size)
        aligned_points = find_num_y_aligned_points(arrow_pixel_coords)
        if aligned_points > max_aligned_points:
            max_aligned_points = aligned_points
            arrow_angle = angle
            best_image_arrow_pixel_coords = arrow_pixel_coords

    if is_pointing_left(best_image_arrow_pixel_coords):
        arrow_angle += 180
    
    print("Angle: {}".format(arrow_angle))
    

if __name__ == "__main__":
    main(sys.argv[1])
En raskere og mer elegant variant, er å plukke ut pixlene til pilen, finne disse punktenes "convex hull" (det polygon bestående av færrest mulig av punktene dine som omslutter alle de andre punktene), og deretter finne de punktene i dette polygonet som har størst avstand mellom hverandre. Det finnes spesielle algoritmer for å finne denne avstanden, men her vil punktene, eller hjørnene, i polygonet være så få at en enkel sjekk av punktenes avstander er umerkbart tregere, om tregere i det hele tatt. For å finne hvilken side av pilen pilspissen sitter på, kan du måle endepunktenes avstand til polygonhjørnenes gjennomsnittsposisjon. Da har du både vinkel og posisjon gitt. Her har jeg skrevet et eksempel:

Kode

import sys
from PIL import Image
from scipy.spatial import ConvexHull
from math import *     # for atan2() and degrees()
import numpy

def sq_distance(point_a, point_b):    
    dx = point_a[0] - point_b[0]
    dy = point_a[1] - point_b[1]
    return dx * dx + dy * dy


def get_most_distant_points(points):
    farthest_dist = 0
    for i, point_a in enumerate(points):
        for point_b in points[i+1:]:
            sq_dist = sq_distance(point_a, point_b)
            if sq_dist > farthest_dist:
                farthest_dist = sq_dist
                farthest_points = [point_a, point_b]
    return farthest_points


def find_angle(points):
    point_a, point_b = points
    dx = point_b[0] - point_a[0]   # Mirrored axis
    dy = point_a[1] - point_b[1]
    deg = degrees(atan2(dy, dx))   # Against horizontal
    while deg < 0:                 # Force positive
        deg += 360
    return deg


def indexes_to_coords(indexes, size):
    coords = [(index % size[0], int(index / size[1])) for index in indexes]
    return coords
    

def find_light_pixel_indexes(pixels):
    indexes = []
    for i, pixel in enumerate(pixels):
        if pixel[0] > 190:
            indexes.append(i)
    return indexes
    

def main(image_file):
    image = Image.open(image_file)
    pixels = image.getdata()

    arrow_pixel_indexes = find_light_pixel_indexes(pixels)
    arrow_pixel_coords = indexes_to_coords(arrow_pixel_indexes, image.size)
    convex_hull = ConvexHull(arrow_pixel_coords)
    vertex_points = convex_hull.points[convex_hull.vertices, :]

    most_distant_points = get_most_distant_points(vertex_points)
    vertex_centroid = numpy.mean(vertex_points, axis=0)
    d0 = sq_distance(vertex_centroid, most_distant_points[0])
    d1 = sq_distance(vertex_centroid, most_distant_points[1])
    if d0 < d1:
        most_distant_points.reverse()

    angle = find_angle(most_distant_points)

    print "Angle: {}".format(angle)
    print "Position: {}".format(most_distant_points[1])


if __name__ == "__main__":
    main(sys.argv[1])
Begge disse løsningene vil for øvrig ha en liten unøyaktighet i at den lengste avstanden internt i pilen vil være fra pilspiss til det ene hjørnet av pilens start. Men om du kan leve med det, bør dette funke helt fint.
Sist endret av Provo; 10. september 2016 kl. 11:05.
Dette ser i grunn superenkelt ut, og du bør kanskje prøve å skrive koden selv?

> Finn en hvit pixel (0xFFFFFF) ved linjesøk
// den du finner vil være enten en del av "pilen" eller begynnelsen på streken... i så fall venstre
> Sjekk alle pixlene rundt i en radius av litt mindre enn lengden på streken med klokken.
> Sjekk på tilsvarende vis at du ikke er "på pilhodet"
> Hvis du ikke er det er retningen lett å finne, hvis ikke let etter enden av "streken" igjen med søk andre veien

... etc.

Da har du maks 4 iterasjoner og du vil vite retning og kan regne ut dx og dy

Hvis du vil ha det til å gå enda raskere kan du optimere "finne enden"-koden, men resten er plankekjøring...



Nå ser Provo sine løsninger flotte ut, men dette ser ut som en skoleoppgave eller lignende?

1) finn hvit pixel (her kan du scanne med f.eks. 10x10 for så å bevege deg tilbake til hjørnet etterpå om du vil ha det raskere)
2) sjekk at det ikke er "pil-enden" ved å sjekke hver n-te pixel i en sirkel rundt og sjekk at den ikke er hvit
3) nå har du andre enden, sjekk hver n-te pixel (n = < 0.5 ganger bredden av pilen) i en sirkel rundt punktet til du finner en hvit pixel og gå derfra til kanten (gå i retning normalen av den ca-linjen du har)

Ved å vite hvilken vei du scannet vil du vite retningen på pilen ved å vite posisjonen til de to pixlene.

God mulig at Provo sin er raskere, men denne er i alle fall så intuitiv at du klarer å følge alt steg for steg
Sist endret av phish; 10. september 2016 kl. 11:57. Grunn: Automatisk sammenslåing med etterfølgende innlegg.
Her er kode som finner pilens retning ved hjelp av OpenCV/SciPy:

Kode

# For bruk i Jupyter Notebook
#%matplotlib inline
#from pylab import rcParams
#rcParams['figure.figsize'] = 18, 18

from __future__ import print_function
import numpy as np
import cv2
import matplotlib.pyplot as plt
from scipy.spatial import ConvexHull
import scipy.stats

# Finn hjørner i bildet.
img2 = cv2.imread('img1_30deg.jpg',0) # 30
#img2 = cv2.imread('img2_120deg.jpg',0) # 156
#img2 = cv2.imread('img3_20deg.jpg',0) # -45
fast2 = cv2.FastFeatureDetector_create()
fast2.setThreshold(100)
fast2.setNonmaxSuppression(True)
kp2 = fast2.detect(img2,None)

img2_kp = cv2.drawKeypoints(img2, kp2, None)
plt.imshow(img2_kp)

pts = np.array(sorted([kp.pt for kp in kp2]))
assert len(pts) == 7, "Try changing threshold"

# Find convex hull
hull = ConvexHull(pts)
for simplex in hull.simplices:
    plt.plot(pts[simplex,0], pts[simplex,1], 'c-')
hullpts = pts[hull.vertices]

# Find centroid
centroid = np.average(hullpts, axis=0)
plt.scatter(centroid[0], centroid[1])

plt.ylim((img2.shape[1],0))
plt.xlim((0, img2.shape[0]))

# Find tip by looping through the corners, looking for equal distance to neighboring points.
assert len(hull.vertices) == 5
for i, vertex in enumerate(hull.vertices):
    tip = hullpts[i]
    neighbors = hullpts[((i-1+5) % 5, (i+1) % 5), :]
    neighbor_dists = np.linalg.norm(tip - neighbors, axis=1)
    if abs(neighbor_dists[0] - neighbor_dists[1]) < 5:
        break
        
# Vector from centroid to tip
plt.plot((centroid[0], tip[0]), (centroid[1], tip[1]))

# Find angle of arrow
p = tip - centroid
print("Arrow direction vector:", p)
print("Angle from right vector, clockwise:", np.degrees(np.arctan2(p[1], p[0])))
http://i.imgur.com/n8dSt4K.png
Arrow direction vector: [-91.6 40.2]
Angle from right vector, clockwise: 156.305028961

http://i.imgur.com/o22CD8Y.png
Arrow direction vector: [ 86.8 51. ]
Angle from right vector, clockwise: 30.4366892715

http://i.imgur.com/mfFbfeX.png
Arrow direction vector: [ 70. -71.4]
Angle from right vector, clockwise: -45.5672664099

Jeg ser for meg at det kommer til å bli problemer på mer komplekse bilder med større kontrast utenom pilen.
Sist endret av Ozma; 10. september 2016 kl. 13:38.
En med Harris corner detection fra scikit-image.
Finner hjørne punkter,
med litt tankegang finner man retning.
@Ozma kult med retning.

Kode

from skimage.feature import(
corner_harris, corner_subpix, corner_peaks)
from skimage import io
import numpy as np
from matplotlib import pyplot as plt

img = io.imread('1.jpg', flatten=True)
coords = corner_peaks(corner_harris(img), min_distance=5)
coords_subpix = corner_subpix(img, coords, window_size=10)
print(coords)
[[221 206]
 [236 198]
 [272 353]
 [296 336]
 [313 326]
 [331 376]
 [340 313]]
Med Plot.

Kode

fig, ax = plt.subplots()
ax.imshow(img, interpolation='nearest', cmap=plt.cm.gray)
ax.plot(coords[:, 1], coords[:, 0], '.b', markersize=3)
ax.plot(coords_subpix[:, 1], coords_subpix[:, 0], '+r', markersize=15)
ax.axis((0, 600, 600, 0))
plt.show()
http://imagizer.imageshack.us/v2/800x600q90/922/XwW3ub.png
Sist endret av snippsat; 10. september 2016 kl. 18:47.
Hmm, når jeg tenker meg om finnes det en enda enklere måte å finne pilas retning.

p1 = snittposisjonen til hjørnene i polygonet
p2 = snittposisjonen til hjørnene i det konvekse hullet

Retningen blir da p1 - p2.
Sitat av Ozma Vis innlegg
Hmm, når jeg tenker meg om finnes det en enda enklere måte å finne pilas retning.

p1 = snittposisjonen til hjørnene i polygonet
p2 = snittposisjonen til hjørnene i det konvekse hullet

Retningen blir da p1 - p2.
Vis hele sitatet...
Tja, det beste er å unngå metoder som unngår kant-deteksjon i det hele tatt. Er bildet blurry kan lengden på de forskjellige, i utgangspunktet rette, linjene variere fryktelig mye.

En mer stabil metode å finne retning på er å finne vektoren fra midterste piksel til gjennonsnittlig pikselposisjon i polygenet.
Tusen takk for alle svar, ser det er flere løsninger her som er bedre enn den jeg fant Jeg endte opp med å filtrere ut pilen, for så å finne de fire punktene lengst fra hverandre parvis, for så ta xy1=(par1[0] + par1[1]) / 2, xy2=(par2[0] + par2[1]) / 2. Godt mulig det ser ut som en skoleoppgave, men her er det bare jeg som leker meg med en idé
Sist endret av stepry; 16. september 2016 kl. 12:42.
Sitat av stepry Vis innlegg
Tusen takk for alle svar, ser det er flere løsninger her som er bedre enn den jeg fant Jeg endte opp med å filtrere ut pilen, for så å finne de fire punktene lengst fra hverandre parvis, for så ta xy1=(par1[0] + par1[1]) / 2, xy2=(par2[0] + par2[1]) / 2. Godt mulig det ser ut som en skoleoppgave, men her er det bare jeg som leker meg med en idé
Vis hele sitatet...
Det hadde ikke vært noe problem om det var en skoleoppgave heller, men siden du i utgangspunktet trodde det var veldig vanskelig, kunne det høres ut som om du ikke var helt "klar" for å følge med på nøyaktig hva som skjer i de mer kompliserte svarene...

Det hadde vært gøy å implementere alle metodene for så å finne ut hvilken som er raskest, men den jeg ga deg er i alle fall raskere enn din, og den er så intuitiv om du ser på tegningen, at det ikke skulle være noe problem å følge tankegangen...

At det går an å gjøre det mer elegant er også vist her, men hobby eller hva det nå er; det er greit å ta litt små steg

Kan du poste metoden der du finner ut hvilket par av punkter som er lengst fra hverandre, aka de to endene på pila?

Jeg er faktisk ikke sikker på hva som er den aller raskeste måten å gjøre det på, gitt oppgaven, og det er fint å prøve å tenke ut den raskeste måten også...

Gøy oppgave Skal du bruke det til noe, eller var det bare for å se om det gikk an?