27 votes

Algorithmes / méthodes suggérés pour disposer des étiquettes sur une image

Donné une image et un ensemble d'étiquettes attachées à des points spécifiques sur l'image, je suis à la recherche d'un algorithme de disposer les étiquettes sur les côtés de l'image, avec certaines contraintes (à peu près le même nombre d'étiquettes sur chaque côté, des étiquettes peu près à égale distance, des lignes reliant les étiquettes de leurs points respectifs avec pas de lignes de passage).

Maintenant, un approximatif solution peut généralement être trouvé assez naïvement par la commande d'étiquettes, Y coordonnées du point, ils font référence), comme dans cet exemple (la preuve de concept que, s'il vous plaît ignorer exactitude ou l'inexactitude des données réelles!).

Maintenant, pour satisfaire à la condition d'absence de passages à niveau, les quelques idées qui m'est venue:

  • utilisation d'un algorithme génétique pour trouver une commande d'étiquettes avec pas de croisements;
  • utiliser une autre méthode (par exemple, algorithme de programmation dynamique) à la recherche d'une telle commande;
  • utilisez l'une des algorithmes ci-dessus, en tenant compte des différences dans l'espacement ainsi que la commande, pour trouver la solution qui minimise le nombre de passages à niveau et de la variation de l'espacement;
  • peut-être il y a des critères que je peux utiliser de la recherche par le biais de tous les possible de commander des étiquettes à l'intérieur de certains critères (ne pas re-commande de deux étiquettes si la distance est supérieure à X);
  • si tout le reste échoue, essayez de millions de hasard rangements/espacement des décalages et de prendre celle qui donne le minimum de passages ou de variation de l'espacement. (Avantage: simple à programmer et trouverez probablement une assez bonne solution; léger désavantage, mais pas un show-bouchon: peut-être ne peut pas ensuite l'exécuter à la volée lors de l'application pour permettre à l'utilisateur de changer de mise en page/taille de l'image.)

Avant de m'embarquer sur l'un de ces, je viens de recevoir quelques commentaires des autres: quelqu'un d'autre a de l'expérience avec un problème similaire, et avoir toutes les informations sur la réussite ou l'échec de l'une des méthodes ci-dessus, ou si elles ont un meilleur/solution plus simple qui n'est pas le lieu pour moi? Merci pour vos commentaires!

13voto

Jivlain Points 2087

Lucas Bradsheet de la thèse de spécialisation de l'Étiquetage des Cartes à l'aide de Multi-Objectif des Algorithmes Évolutionnaires a tout à fait une bonne discussion de ce.

Tout d'abord, le présent article crée utilisable métriques pour un certain nombre de critères de l'étiquetage de la qualité.

Par exemple, la clarté (de façon évidente le mappage entre les sites et les étiquettes): la clarté(s)=rs+rs1/rt
où rs est la distance entre un site et son étiquette et rt est la distance entre un site et il y a de plus proche d'autres l'étiquette).

Il a aussi utile métriques pour les conflits entre les étiquettes, des sites et des frontières, ainsi que pour la mesure de la densité et de la symétrie des étiquettes. Bradsheet utilise alors un multiple de l'objectif de l'algorithme génétique pour générer une "frontière de Pareto" des solutions réalisables. Il comprend également des informations sur la façon dont il a muté les individus, et quelques notes sur l'amélioration de la vitesse de l'algorithme.

Il y a beaucoup de détail, et il devrait fournir une bonne nourriture pour la pensée.

8voto

Renat Gilmanov Points 9287

Je pense que d'une réelle solution de ce problème est légèrement différent de la couche. Il ne semble pas être un bonne idée de commencer à résoudre algorithmique problème ignorant totalement la conception de l'Information. Il y a un exemple intéressant trouvé ici

Nous allons identifier certaines questions importantes:

  1. Comment les données sont mieux vus?
  2. Il va embrouiller les gens?
  3. Est-il lisible?
  4. Est-ce que ça aide à mieux comprendre l'image?

Par la manière, le chaos est vraiment déroutant. On aime l'ordre et de la prévisibilité. Il n'est pas nécessaire d'introduire le bruit informationnel à l'image initiale.

enter image description here

La lisibilité d'un message graphique est déterminé par le contenu et sa présentation. La lisibilité d'un message implique la capacité du lecteur à comprendre le style du texte et des images. Vous avez intéressant algorithmique tâche en raison de l'augmentation des "bruits" de la démarche. Retirez le chaos -- trouver la meilleure solution :)

enter image description here

Veuillez noter, c'est juste un PoC. L'idée est d'utiliser uniquement des lignes horizontales avec des repères clairs. Les étiquettes de placement est simple et déterministe. Plusieurs idées peuvent être proposées.

enter image description here

Avec cette approche, vous pouvez facilement la balance gauche-droite des étiquettes, éviter les petits verticale lacunes entre les lignes, de façon optimale, en verticale de la densité pour les étiquettes, etc.

MODIFIER

Ok, nous allons voir comment le processus initial peut regarder.

Article de l'utilisateur: en tant qu'utilisateur, je veux les images importantes pour être annoté afin de simplifier la compréhension et de l'augmenter de valeur explicative.

Les principales hypothèses:

  1. l'image initiale est un objectif principal pour l'utilisateur
  2. la lisibilité est un must

Donc, la meilleure solution possible est d'avoir des annotations, mais ne les ont pas. (Je suggère de passer un peu de temps à lire à propos de la théorie de l'inventif de résolution de problème).

Fondamentalement, il devrait y avoir aucun obstacle pour l'utilisateur de voir l'image initiale, mais les annotations devraient être là en cas de besoin. Il peut être un peu confus, désolé pour ça.

Pensez-vous intersections question est seulement l'un derrière l'image suivante?

enter image description here

Veuillez noter que le véritable objectif derrière l'approche développée est de fournir deux flux d'informations (image et annotations) et d'aider l'utilisateur à comprendre tout aussi rapide que possible. Par la manière, de la vision, de la mémoire est également très important.

Quelles sont derrière la vision humaine:

  1. L'attention sélective
  2. La familiarité de détection
  3. Modèle de détection

Voulez-vous casser au moins un de ces mécanismes? J'espère que vous n'avez pas. Parce qu'il va rendre le résultat n'est pas très convivial.

Donc ce qui peut me distraire?

  1. lignes étranges distribués de façon aléatoire sur l'image (aléatoire objets géométriques sont très amusants)
  2. pas uniforme annotations de placement et de style
  3. d'étranges motifs complexes comme un résultat de la finale de la fusion de l'image et de la couche d'annotations

Pourquoi ma proposition devrait être examinée?

  1. Il a modèle simple, de sorte que la détection va permettre à l'utilisateur d'arrêter de remarquer les annotations, mais voir l'image à la place
  2. Il a conception uniforme, de sorte que la familiarité de détection sera trop de travail
  3. Il n'affecte pas l'image initiale autant que d'autres solutions, parce que les lignes ont un minimum de largeur.
  4. Les lignes sont horizontales, l'anti-aliasing n'est pas utilisé, il permet d'économiser plus d'informations et fournit propre résultat
  5. Enfin, il ne simplifient algorithme de routage beaucoup.

Quelques commentaires supplémentaires:

  1. N'utilisez pas de points aléatoires pour tester vos algorithmes, l'utilisation simple mais important de cas. Vous verrez des solutions automatisées peuvent parfois échouer de façon spectaculaire.
  2. Je ne suggère l'utilisation de l'approche proposée par moi comme est. Il y a beaucoup d'améliorations possibles.
  3. Ce que je suis vraiment suggérer est de remonter d'un niveau et faire plusieurs itérations sur le méta-niveau.

Le groupement peut être utilisé pour traiter les cas complexes, mentionné par Robert King:

enter image description here

Ou je peux imaginer une seconde un certain point est situé légèrement au-dessus de l'emplacement par défaut. Mais seulement pour une seconde, parce que je ne veux pas briser les principaux flux de traitement et d'affecter d'autres marqueurs.

enter image description here

Merci pour la lecture.

8voto

Renat Gilmanov Points 9287

Oublions de conception d'information pour un moment. Cette tâche rappelle certains souvenirs sont liés à des algorithmes de routage PCB. En fait, il ya beaucoup d'exigences communes, y compris:

  1. les intersections de l'optimisation
  2. l'optimisation de la taille
  3. les lacunes de l'optimisation

Aussi, il pourrait être possible de tourner la tâche initiale en quelque chose de semblable à PCB routage.

Il y a beaucoup d'informations disponibles, mais je suggère de regarder à travers Algorithmique des études sur les PCB routage par Tan Yan.

Il fournit beaucoup de détails et des dizaines de trucs.

Adaptation pour la tâche en cours

L'idée est de traiter des marqueurs sur l'image et les étiquettes que les deux ensembles de broches et de l'utilisation échapper de routage pour résoudre la tâche. Habituellement, les PCB espace est représenté comme un tableau de pins. La même chose peut être fait à l'image avec des optimisations possibles:

  1. éviter les zones de faible contraste
  2. éviter les zones de texte si tout
  3. etc

Donc, la tâche peut être réduite à "routage en cas de broches non utilisées"

enter image description here

Résultat Final peut être vraiment proche de la demande du style:

enter image description here

Algorithmique des études sur les PCB routage par Tan Yan est un bon endroit pour continuer.

Notes supplémentaires

Je chn changer le style de dessin un peu, afin d'accentuer la ressemblance.

enter image description here

Il ne devrait pas être un gros problème pour faire quelques transformations inverse, en gardant le bon coup d'oeil et la lisibilité.

enter image description here

De toute façon, les adeptes de la simplicité (comme moi, par exemple) peuvent passer plusieurs minutes et d'inventer quelque chose de mieux (ou de quelque chose de différent):

enter image description here

Comme pour moi, les courbes ne ressemble pas à une solution complète, au moins à ce stade. De toute façon, j'ai juste essayé de montrer qu'il y a place pour des améliorations, de sorte PCB routage approche peut être considérée comme une option.

7voto

robert king Points 5369

Une option est de le transformer en un entier problème de programmation.

Disons que vous avez n points et n corresponding labels distribués autour de l'extérieur du diagramme.

Le nombre de lignes est - n^2, si l'on regarde toutes les combinaisons possibles, il y a moins de n^4 intersections (si possible toutes les lignes ont été affichée).

Dans notre programmation en nombres entiers problème, nous ajoutons les contraintes suivantes:

(afin de décider si une ligne est activé (c'est à dire affichées à l'écran) )

  1. Pour chaque point sur le diagramme, seulement l'un des n lignes la connexion est allumé.

  2. Pour chaque étiquette, seulement de l'un des n lignes de la connexion à il est à être allumé.

  3. Pour chaque paire de ligne d'intersection des segments ligne1 et ligne2, seulement zéro ou de l'une de ces lignes peut être allumé.

  4. En option, nous pouvons minimiser la distance totale de tous les commuté sur les lignes. Cela améliore l'esthétique.

Lorsque toutes ces contraintes tenir dans le même temps, nous avons une solution:

enter image description here

Le code ci-dessous produit le schéma ci-dessus pour 24 points aléatoires.

Une fois que Vous commencez à obtenir plus de 15 points, le temps d'exécution du programme va commencer à ralentir.

J'ai utilisé de la PÂTE de paquet avec le son par défaut du résolveur. J'ai utilisé PyGame pour l'affichage.

Voici le code:

__author__ = 'Robert'

import pygame
pygame.font.init()
import pulp
from random import randint

class Line():
    def __init__(self, p1, p2):
        self.p1 = p1
        self.p2 = p2
        self.length = (p1[0] - p2[0])**2 + (p1[1] - p2[1])**2

    def intersect(self, line2):
        #Copied some equations for wikipedia. Not sure if this is the best way to check intersection.
        x1, y1 = self.p1
        x2, y2 = self.p2
        x3, y3 = line2.p1
        x4, y4 = line2.p2
        xtop = (x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4)
        xbottom = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4)
        ytop = (x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4)
        ybottom = xbottom
        if xbottom == 0:
            #lines are parallel. Can only intersect if they are the same line. I'm not checking that however,
            #which means there could be a rare bug that occurs if more than 3 points line up.
            if self.p1 in (line2.p1, line2.p2) or self.p2 in (line2.p1, line2.p2):
                return True
            return False
        x = float(xtop) / xbottom
        y = float(ytop) / ybottom
        if min(x1, x2) <= x <= max(x1, x2) and min(x3, x4) <= x <= max(x3, x4):
            if min(y1, y2) <= y <= max(y1, y2) and min(y3, y4) <= y <= max(y3, y4):
                return True
        return False

def solver(lines):
    #returns best line matching
    lines = list(lines)
    prob = pulp.LpProblem("diagram labelling finder", pulp.LpMinimize)

    label_points = {} #a point at each label
    points = {} #points on the image
    line_variables = {}
    variable_to_line = {}

    for line in lines:
        point, label_point = line.p1, line.p2
        if label_point not in label_points:
            label_points[label_point] = []
        if point not in points:
            points[point] = []
        line_on = pulp.LpVariable("point{0}-point{1}".format(point, label_point),
            lowBound=0, upBound=1, cat=pulp.LpInteger) #variable controls if line used or not
        label_points[label_point].append(line_on)
        points[point].append(line_on)
        line_variables[line] = line_on
        variable_to_line[line_on] = line

    for lines_to_point in points.itervalues():
        prob += sum(lines_to_point) == 1 #1 label to each point..

    for lines_to_label in label_points.itervalues():
        prob += sum(lines_to_label) == 1 #1 point for each label.

    for line1 in lines:
        for line2 in lines:
            if line1 > line2 and line1.intersect(line2):
                line1_on = line_variables[line1]
                line2_on = line_variables[line2]
                prob += line1_on + line2_on  <= 1 #only switch one on.

    #minimize length of switched on lines:
    prob += sum(i.length * line_variables[i] for i in lines)

    prob.solve()
    print prob.solutionTime
    print pulp.LpStatus[prob.status] #should say "Optimal"
    print len(prob.variables())

    for line_on, line in variable_to_line.iteritems():
        if line_on.varValue > 0:
            yield line #yield the lines that are switched on

class Diagram():
    def __init__(self, num_points=20, width=700, height=800, offset=150):
        assert(num_points % 2 == 0) #if even then labels align nicer (-:
        self.background_colour = (255,255,255)
        self.width, self.height = width, height
        self.screen = pygame.display.set_mode((width, height))
        pygame.display.set_caption('Diagram Labeling')
        self.screen.fill(self.background_colour)
        self.offset = offset
        self.points = list(self.get_points(num_points))
        self.num_points = num_points
        self.font_size = min((self.height - 2 * self.offset)//num_points, self.offset//4)

    def get_points(self, n):
        for i in range(n):
            x = randint(self.offset, self.width - self.offset)
            y = randint(self.offset, self.height - self.offset)
            yield (x, y)

    def display_outline(self):
        w, h = self.width, self.height
        o = self.offset
        outline1 = [(o, o), (w - o, o), (w - o, h - o), (o, h - o)]
        pygame.draw.lines(self.screen, (0, 100, 100), True, outline1, 1)
        o = self.offset - self.offset//4
        outline2 = [(o, o), (w - o, o), (w - o, h - o), (o, h - o)]
        pygame.draw.lines(self.screen, (0, 200, 0), True, outline2, 1)

    def display_points(self, color=(100, 100, 0), radius=3):
        for point in self.points:
            pygame.draw.circle(self.screen, color, point, radius, 2)

    def get_label_heights(self):
        for i in range((self.num_points + 1)//2):
            yield self.offset + 2 * i * self.font_size

    def get_label_endpoints(self):
        for y in self.get_label_heights():
            yield (self.offset, y)
            yield (self.width - self.offset, y)

    def get_all_lines(self):
        for point in self.points:
            for end_point in self.get_label_endpoints():
                yield Line(point, end_point)


    def display_label_lines(self, lines):
        for line in lines:
            pygame.draw.line(self.screen, (255, 0, 0), line.p1, line.p2, 1)

    def display_labels(self):
        myfont = pygame.font.SysFont("Comic Sans MS", self.font_size)
        label = myfont.render("label", True, (155, 155, 155))
        for y in self.get_label_heights():
            self.screen.blit(label, (self.offset//4 - 10, y - self.font_size//2))
            pygame.draw.line(self.screen, (255, 0, 0), (self.offset -  self.offset//4, y), (self.offset, y), 1)
        for y in self.get_label_heights():
            self.screen.blit(label, (self.width - 2*self.offset//3, y - self.font_size//2))
            pygame.draw.line(self.screen, (255, 0, 0), (self.width - self.offset + self.offset//4, y), (self.width - self.offset, y), 1)

    def display(self):
        self.display_points()
        self.display_outline()
        self.display_labels()
        #self.display_label_lines(self.get_all_lines())
        self.display_label_lines(solver(self.get_all_lines()))

diagram = Diagram()
diagram.display()

pygame.display.flip()
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

4voto

AJMansfield Points 2147

Vous pouvez trouver le centre de votre diagramme, puis tracer les lignes de points radialement vers l'extérieur à partir du centre. La seule façon, vous pourriez avoir un passage à niveau est si deux de ces points se trouvent sur le même rayon, dans ce cas, vous passez de l'une des lignes de un peu une façon, de décalage et de l'autre un peu dans l'autre sens, comme ceci:

Centerlines

Avec seulement les parties réelles montrant:

All Done

Dans le cas où il y a deux points ou plus colinéaire avec le centre, vous pouvez déplacer les lignes légèrement sur le côté:

In case of colinearity

Bien que ce ne débite pas produire de très bonne multi-ligne les choses, c'est très clairement les étiquettes le diagramme. Aussi, afin de le rendre plus fisually attrayant, il peut être préférable de choisir un point central qui est en fait le centre de votre objet, plutôt que de simplement le centre de l'ensemble de points.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X