2010-10-13 5 views
5

que tiene un directorio de 9 imágenes:¿Cómo hacer una lista de una secuencia de imágenes de manera eficiente? comparación de secuencias Numercial en Python

 
image_0001, image_0002, image_0003 
image_0010, image_0011 
image_0011-1, image_0011-2, image_0011-3 
image_9999 

me gustaría ser capaz de enumerarlos de una manera eficiente, como esto (4 entradas para 9 imágenes):

 
(image_000[1-3], image_00[10-11], image_0011-[1-3], image_9999) 

¿Hay alguna forma en python para devolver un directorio de imágenes, de forma breve/clara (sin incluir todos los archivos)?

Por lo tanto, posiblemente algo como esto:

lista de todas las imágenes, clasificar numéricamente, crear una lista (contando cada imagen de la secuencia de inicio). Cuando falta una imagen (crea una nueva lista), continúa hasta que la lista de archivos original haya finalizado. Ahora debería tener algunas listas que contienen secuencias no rotas.

Estoy tratando de facilitar la lectura/descripción de una lista de números. Si tuviera una secuencia de 1000 archivos consecutivos, podría aparecer claramente como archivo [0001-1000] en lugar de archivo ['0001', '0002', '0003', etc.]

Edit1 (basado por sugerencia): dada una lista aplanada, ¿cómo derivaría los patrones glob?

Edit2 Estoy tratando de dividir el problema en pedazos más pequeños. Aquí es un ejemplo de parte de la solución: obras datos1, vuelve Data2 0010 como 64, Data3 (los datos del mundo real) no funciona:

# Find runs of consecutive numbers using groupby. The key to the solution 
# is differencing with a range so that consecutive numbers all appear in 
# same group. 
from operator import itemgetter 
from itertools import * 

data1=[01,02,03,10,11,100,9999] 
data2=[0001,0002,0003,0010,0011,0100,9999] 
data3=['image_0001','image_0002','image_0003','image_0010','image_0011','image_0011-2','image_0011-3','image_0100','image_9999'] 

list1 = [] 
for k, g in groupby(enumerate(data1), lambda (i,x):i-x): 
    list1.append(map(itemgetter(1), g)) 
print 'data1' 
print list1 

list2 = [] 
for k, g in groupby(enumerate(data2), lambda (i,x):i-x): 
    list2.append(map(itemgetter(1), g)) 
print '\ndata2' 
print list2 

rendimientos:

data1 
[[1, 2, 3], [10, 11], [100], [9999]] 

data2 
[[1, 2, 3], [8, 9], [64], [9999]] 
+0

Por qué 'image_00 [10-11]' 'y no Image_001 [0-1] '? – eumiro

+0

image_00 [10-11] o image_001 [0-1], sí, supongo que es un personaje menos – user178686

+0

Cínicamente: sí, hay una manera. Dudo (pero podría estar equivocado) que haya alguna función de biblioteca para hacer esto. Escriba un código, pregunte algo más específico (por ejemplo, ¿cómo puedo comparar cadenas de similitud) después de que ya hizo 'os.listdir (ruta)', etc. –

Respuesta

6

Aquí es una aplicación de lo que quiere trabajar para lograr, mediante el código añadido como punto de partida:

#!/usr/bin/env python 

import itertools 
import re 

# This algorithm only works if DATA is sorted. 
DATA = ["image_0001", "image_0002", "image_0003", 
     "image_0010", "image_0011", 
     "image_0011-1", "image_0011-2", "image_0011-3", 
     "image_0100", "image_9999"] 

def extract_number(name): 
    # Match the last number in the name and return it as a string, 
    # including leading zeroes (that's important for formatting below). 
    return re.findall(r"\d+$", name)[0] 

def collapse_group(group): 
    if len(group) == 1: 
     return group[0][1] # Unique names collapse to themselves. 
    first = extract_number(group[0][1]) # Fetch range 
    last = extract_number(group[-1][1]) # of this group. 
    # Cheap way to compute the string length of the upper bound, 
    # discarding leading zeroes. 
    length = len(str(int(last))) 
    # Now we have the length of the variable part of the names, 
    # the rest is only formatting. 
    return "%s[%s-%s]" % (group[0][1][:-length], 
     first[-length:], last[-length:]) 

groups = [collapse_group(tuple(group)) \ 
    for key, group in itertools.groupby(enumerate(DATA), 
     lambda(index, name): index - int(extract_number(name)))] 

print groups 

Esto imprime ['image_000[1-3]', 'image_00[10-11]', 'image_0011-[1-3]', 'image_0100', 'image_9999'], que es lo que desea.

HISTORIAL: Inicialmente respondí la pregunta al revés, como @Mark Ransom señaló a continuación. Por el bien de la historia, mi respuesta original fue:

Está buscando glob. Proveedores:

import glob 
images = glob.glob("image_[0-9]*") 

O, usando el ejemplo:

images = [glob.glob(pattern) for pattern in ("image_000[1-3]*", 
    "image_00[10-11]*", "image_0011-[1-3]*", "image_9999*")] 
images = [image for seq in images for image in seq] # flatten the list 
+0

Creo que esta solución está al revés de lo que la pregunta está haciendo. Dada una lista aplanada, ¿cómo obtendrías los patrones glob? –

+0

@Mark, tienes razón, entendí mal la pregunta (y su título realmente debería ser "Dada una lista aplanada, ¿cómo obtendrías los patrones glob?"). Creo que dormiré un poco antes de intentarlo de nuevo:] –

+0

@ Frédéric @Mark. Gracias a ambos por su ayuda. Realmente estoy disfrutando este problema. Estoy aprendiendo sobre la marcha. – user178686

2
def ranges(sorted_list): 
    first = None 
    for x in sorted_list: 
     if first is None: 
      first = last = x 
     elif x == increment(last): 
      last = x 
     else: 
      yield first, last 
      first = last = x 
    if first is not None: 
     yield first, last 

La función increment se deja como ejercicio para el lector.

Edit: aquí hay un ejemplo de cómo se usaría con enteros en lugar de cadenas como entrada.

def increment(x): return x+1 

list(ranges([1,2,3,4,6,7,8,10])) 
[(1, 4), (6, 8), (10, 10)] 

Para cada rango contiguo en la entrada que conseguir un par que indica el inicio y el final del rango.Si un elemento no forma parte de un rango, los valores de inicio y fin son idénticos.

+0

gracias, realmente no entiendo. Entonces, supongamos que clasifiqué los archivos en una lista: sorted_list = ['image_0001', 'image_0002', 'image_0003', 'image_0010', 'image_0011'] ... puede explicar lo que me ha mostrado. Para cada elemento en sorted_list (si lo incrementa, verifique si existe en el resto de la lista) ??? – user178686

+1

@user, este algoritmo prueba cada elemento para ver si debe incluirse en la secuencia actual al probar para ver si es igual a last + 1. Si lo es, entonces la secuencia actual se extiende; de lo contrario, la secuencia se produce como una tupla y la secuencia actual se restablece al nuevo elemento. Si podemos asegurar que la entrada no está vacía, esto podría incluso simplificarse. –

+0

Gracias. Ok, entonces entiendo que prueba cada elemento para ver si es igual al elemento anterior + 1. No entiendo "de lo contrario la secuencia se produce como una tupla" ... – user178686

3

Bien, entonces encontré que su pregunta era un acertijo fascinante. He dejado cómo "comprimir" los rangos numéricos hasta usted (marcado como HACER), ya que hay maneras diferentes de lograr eso dependiendo de cómo te gusta el formato y si desea la cantidad mínima de elementos o la descripción mínima de cadena longitud.

Esta solución usa una expresión regular simple (cadenas de dígitos) para clasificar cada cadena en dos grupos: estático y variable. Después de clasificar los datos, utilizo groupby para recopilar los datos estáticos en los grupos de mayor coincidencia para lograr el efecto de resumen. Mezclo sintinales de índices enteros en el resultado (en matchGrouper) para poder volver a seleccionar las partes variables de todos los elementos (en descomprimir).

import re 
import glob 
from itertools import groupby 
from operator import itemgetter 

def classifyGroups(iterable, reObj=re.compile('\d+')): 
    """Yields successive match lists, where each item in the list is either 
    static text content, or a list of matching values. 

    * `iterable` is a list of strings, such as glob('images/*') 
    * `reObj` is a compiled regular expression that describes the 
      variable section of the iterable you want to match and classify 
    """ 
    def classify(text, pos=0): 
     """Use a regular expression object to split the text into match and non-match sections""" 
     r = [] 
     for m in reObj.finditer(text, pos): 
      m0 = m.start() 
      r.append((False, text[pos:m0])) 
      pos = m.end() 
      r.append((True, text[m0:pos])) 
     r.append((False, text[pos:])) 
     return r 

    def matchGrouper(each): 
     """Returns index of matches or origional text for non-matches""" 
     return [(i if t else v) for i,(t,v) in enumerate(each)] 

    def unpack(k,matches): 
     """If the key is an integer, unpack the value array from matches""" 
     if isinstance(k, int): 
      k = [m[k][1] for m in matches] 
     return k 

    # classify each item into matches 
    matchLists = (classify(t) for t in iterable) 

    # group the matches by their static content 
    for key, matches in groupby(matchLists, matchGrouper): 
     matches = list(matches) 
     # Yield a list of content matches. Each entry is either text 
     # from static content, or a list of matches 
     yield [unpack(k, matches) for k in key] 

Finalmente, agregamos la lógica suficiente para realizar una impresión bonita de la salida, y ejecutamos un ejemplo.

def makeResultPretty(res): 
    """Formats data somewhat like the question""" 
    r = [] 
    for e in res: 
     if isinstance(e, list): 
      # TODO: collapse and simplify ranges as desired here 
      if len(set(e))<=1: 
       # it's a list of the same element 
       e = e[0] 
      else: 
       # prettify the list 
       e = '['+' '.join(e)+']' 
     r.append(e) 
    return ''.join(r) 

fnList = sorted(glob.glob('images/*')) 
re_digits = re.compile(r'\d+') 
for res in classifyGroups(fnList, re_digits): 
    print makeResultPretty(res) 

Mi directorio de imágenes se creó a partir de su ejemplo. Puede reemplazar fnlist con la siguiente lista para la prueba:

fnList = [ 
'images/image_0001.jpg', 
'images/image_0002.jpg', 
'images/image_0003.jpg', 
'images/image_0010.jpg', 
'images/image_0011-1.jpg', 
'images/image_0011-2.jpg', 
'images/image_0011-3.jpg', 
'images/image_0011.jpg', 
'images/image_9999.jpg'] 

Y cuando corro contra este directorio, mi salida se ve como:

StackOverflow/3926936% python classify.py 
images/image_[0001 0002 0003 0010].jpg 
images/image_0011-[1 2 3].jpg 
images/image_[0011 9999].jpg 
+0

Gracias, no estoy seguro de lo que estás haciendo. ¿Podría agregar algunos comentarios que me ayuden a relacionarme con el ejemplo image_0002, image_0003, etc.? Si pudiera agregar una lista de prueba, podría pasar y ejecutar su solución poco a poco. – user178686

+0

GRACIAS por su tiempo, Shane. Continuaré mirando su solución itertools; Creo que puedo aprender mucho de eso. Edit2 en la publicación original fue el resultado de buscar/estudiar en Google tu solución bien comentada. – user178686

Cuestiones relacionadas