Sida 1 av 1

Sorteringsproblem i Python

Postat: 28 apr 2010, 19:26
av BadOmen
Hej,
jag har gjort ett skript som söker igenom exif datan för bilderna i en mapp med dess undermappar och hämtar ut datum och tid för när bilden togs. Nu har jag lagt hela sökvägen till bilderna och datumet och tiden när det skapades i en dictionary. Ni kan se nedan hur det visas i terminalen.

Det jag nu vill göra är att sortera dictionaryn så att den visar bilderna i kronologisk ordning, kanske omvänd ordning så att närmast promten blir den senast tagna bilden.
Hur ska jag göra för att sortera det?

(obs, indragningarna visas tyvärr fel trots att det ser rätt ut när inläggets skrivs så koden funkar nog inte rakt av.)
EDIT ***KODEN ÄR FEL... KOLLA MITT NÄSTA INLÄGG...***

Kod: Markera allt

#!/usr/bin/env python
import os
from PIL import Image
dicImg= {}
i=1
EXTENSIONS = ('.jpg', '.JPG', '.jpeg')
for root, dirs, files in os.walk('/home/jonas/temp/bildtest/testbilderna'):
     for name in files:
        # print  os.path.join(root, name)
	       im=Image.open(os.path.join(root, name))
	       for ext in EXTENSIONS:
			if name.endswith(ext):
				if hasattr(im, '_getexif'):

					exifdata = im._getexif()
            				ctime = exifdata[0x0132]
					#print name," ", ctime
					
					dicImg[i]=[ (os.path.join(root, name)), (ctime) ]
					print dicImg[i]
					i=+1
./exifPILjonas.py
['/home/jonas/temp/bildtest/testbilderna/DSC_3983.JPG', '2010:01:05 15:54:10']
['/home/jonas/temp/bildtest/testbilderna/DSC_6669.JPG', '2010:04:16 08:49:58']
['/home/jonas/temp/bildtest/testbilderna/DSC_6443.JPG', '2010:04:05 15:26:50']
['/home/jonas/temp/bildtest/testbilderna/DSC_6449.JPG', '2010:04:05 15:27:01']
['/home/jonas/temp/bildtest/testbilderna/undermapp/bildpamig.JPG', '2010:04:05 15:26:50']
EDIT: Jag märkte just att jag har gjort dictionaryn helt fel... Jag får ta och kolla koden i morgon, som det är nu så funkar det inte alls som jag hade tänkt... och dessutom så måste jag tydligen skriva i=i+1

Re: Sorteringsproblem i Python

Postat: 28 apr 2010, 22:05
av BadOmen
Nu tror jag koden stämmer bättre överäns med vad jag hade tänkt... men jag vet fortfarande inte hur jag ska kunna sorera den efter datumet och tiden.

Kod: Markera allt

#!/usr/bin/env python
import os
from PIL import Image

dicImg= {}
EXTENSIONS = ('.jpg', '.JPG', '.jpeg')
for root, dirs, files in os.walk('/home/jonas/temp/bildtest/testbilderna'):
     for name in files:
        # print  os.path.join(root, name)
          im=Image.open(os.path.join(root, name))
          for ext in EXTENSIONS:
				if name.endswith(ext):
					if hasattr(im, '_getexif'):
						exifdata = im._getexif()
						ctime = exifdata[0x0132]
            					dicImg[(os.path.join(root, name))]= ctime
print dicImg
Så här skrivs datan ut i terminalen:
./exifPILjonas.py
{'/home/jonas/temp/bildtest/testbilderna/DSC_3983.JPG': '2010:01:05 15:54:10', '/home/jonas/temp/bildtest/testbilderna/DSC_6443.JPG': '2010:04:05 15:26:50', '/home/jonas/temp/bildtest/testbilderna/DSC_6449.JPG': '2010:04:05 15:27:01', '/home/jonas/temp/bildtest/testbilderna/DSC_6669.JPG': '2010:04:16 08:49:58', '/home/jonas/temp/bildtest/testbilderna/undermapp/bildpmig.JPG': '2010:04:05 15:26:50'}
EDIT: jag har lyckats sortera med hjälp av den här:

Kod: Markera allt

def sort_by_value(d):
    """ Returns the keys of dictionary d sorted by their values """
    items=d.items()
    backitems=[ [v[1],v[0]] for v in items]
    backitems.sort()
    return [ backitems[i][1] for i in range(0,len(backitems))]
Jag laddar upp den kompletta koden en annan dag, nu är jag så trött att jag ser i kors och klockan är bara 23! Jag tror jag håller på bli gubbig :)

Re: Sorteringsproblem i Python

Postat: 29 apr 2010, 06:15
av Substrata
Kan inte Python, men 'ctime' ser ut att ha ett strängvärde. Vanligtvis är sorteringsnyckeln ett heltal, även om du i det här fallet bör kunna sortera strängarna direkt för att avgöra vilken av två filer som är den senare.
http://docs.python.org/library/functions.html?highlight=sort#sorted skrev: sorted(iterable[, cmp[, key[, reverse]]])
Return a new sorted list from the items in iterable.

The optional arguments cmp, key, and reverse have the same meaning as those for the list.sort() method (described in section Mutable Sequence Types).

cmp specifies a custom comparison function of two arguments (iterable elements) which should return a negative, zero or positive number depending on whether the first argument is considered smaller than, equal to, or larger than the second argument: cmp=lambda x,y: cmp(x.lower(), y.lower()). The default value is None.

key specifies a function of one argument that is used to extract a comparison key from each list element: key=str.lower. The default value is None (compare the elements directly).
Så det ser ut som att du i första hand använder key-argumentet för att plocka ut datumsträngen ur sekvensen. Nu ser jag dock att det verkar vara just vad du har gjort (för hand), men du borde kunna förenkla sort_by_value till en rad. Dock vet jag inte hur det här fungerar med en dictionary, som ju är fundamentalt "osorterad" (dvs, strukturen har en intern sortering som inte påminner om din).

Re: Sorteringsproblem i Python

Postat: 29 apr 2010, 06:59
av dmz
Man kan väl sortera efter key/value helt enkelt?

Re: Sorteringsproblem i Python

Postat: 29 apr 2010, 19:22
av BadOmen
dmz skrev:Man kan väl sortera efter key/value helt enkelt?
dictionarys har tyvärr inget bra sätt att sortera direkt key/value man måste tyvärr böka lite för att å det att funka som man vill.

Re: Sorteringsproblem i Python

Postat: 29 apr 2010, 19:50
av BadOmen
Substrata skrev:Kan inte Python, men 'ctime' ser ut att ha ett strängvärde. Vanligtvis är sorteringsnyckeln ett heltal, även om du i det här fallet bör kunna sortera strängarna direkt för att avgöra vilken av två filer som är den senare.
http://docs.python.org/library/functions.html?highlight=sort#sorted skrev: sorted(iterable[, cmp[, key[, reverse]]])
Return a new sorted list from the items in iterable.

The optional arguments cmp, key, and reverse have the same meaning as those for the list.sort() method (described in section Mutable Sequence Types).

cmp specifies a custom comparison function of two arguments (iterable elements) which should return a negative, zero or positive number depending on whether the first argument is considered smaller than, equal to, or larger than the second argument: cmp=lambda x,y: cmp(x.lower(), y.lower()). The default value is None.

key specifies a function of one argument that is used to extract a comparison key from each list element: key=str.lower. The default value is None (compare the elements directly).
Så det ser ut som att du i första hand använder key-argumentet för att plocka ut datumsträngen ur sekvensen. Nu ser jag dock att det verkar vara just vad du har gjort (för hand), men du borde kunna förenkla sort_by_value till en rad. Dock vet jag inte hur det här fungerar med en dictionary, som ju är fundamentalt "osorterad" (dvs, strukturen har en intern sortering som inte påminner om din).
Det gick att använda den men den returnerar en lista med tuples så det blir en lista som ser ut så här [[bildnamn,datum],[bildnamn,datum]] så om jag skulle vilja komma åt endast datumdelen blir det strul iaf. Så här använde jag den:

Kod: Markera allt

sortedImg=sorted(dicImg.items(), key=lambda (k,v): (v,k))
Jag kör med "sort_by_value(d)" i stället, då har jag fler valmöjligheter, så här ser min kod ut nu och den skriver ut det som jag vill och jag har en bra grund att bygga vidare på :)

Kod: Markera allt

#!/usr/bin/env python
import os
from PIL import Image

 
def sort_by_value(d):
    """ Returns the keys of dictionary d sorted by their values """
    items=d.items()
    backitems=[ [v[1],v[0]] for v in items]
    backitems.sort()
    return [ backitems[i][1] for i in range(0,len(backitems))]


dicImg= {}
EXTENSIONS = ('.jpg', '.JPG', '.jpeg')
for root, dirs, files in os.walk('/home/jonas/temp/bildtest/testbilderna'):
     for name in files:
          im=Image.open(os.path.join(root, name))
          for ext in EXTENSIONS:
				if name.endswith(ext) and hasattr(im, '_getexif'):
						exifdata = im._getexif()
						ctime = exifdata[0x0132]
						dicImg[(os.path.join(root, name))]= ctime
						
arrImgName = sort_by_value(dicImg)
x=0
while x < len(arrImgName):
	
	print arrImgName[x],  dicImg[arrImgName[x]] #prints name and path + date and time (photo is taken).
	x=x+1 

Re: Sorteringsproblem i Python

Postat: 29 apr 2010, 21:33
av Substrata
BadOmen skrev:Det gick att använda den men den returnerar en lista med tuples så det blir en lista som ser ut så här [[bildnamn,datum],[bildnamn,datum]] så om jag skulle vilja komma åt endast datumdelen blir det strul iaf.
Har åtminstone fått anledning att labba lite i Python :) Här sorteras en lista med namn/heltal-par efter heltal och ger namnen:

Kod: Markera allt

a = [("a", 1), ("c", 3), ("b", 2)]
map(lambda v: v[0], sorted(a, key=lambda v: v[1]))

=> ['a', 'b', 'c']
Vet dock inte riktigt vad du ska ha en dictionary till. Ur manualen: There are six sequence types: strings, Unicode strings, lists, tuples, buffers, and xrange objects. Dictionary verkar alltså inte vara en sekvenstyp, som sort() och sorted() är definierad över. Faktum är att sorted(dict, key=f) beter sig som sorted(dict.keys, key=f) när den skickar argumentet till f:

Kod: Markera allt

>>> def p(*t):
...     print t
...     True
... 
>>> a = {"a": 1, "c": 3, "b": 2}
>>> sorted(a, key=p)
('a',)
('c',)
('b',)
['a', 'c', 'b'] # returvärdet...
Så det ser inte ut som en bra lösning att använda dictionaries i det här fallet. Bättre i så fall att använda en lista med tuples, eller en lista med objekt om du vill knyta mer avancerad logik till bild/datum-representeringen.

Re: Sorteringsproblem i Python

Postat: 02 maj 2010, 11:48
av BadOmen
Substrata skrev:
BadOmen skrev:Det gick att använda den men den returnerar en lista med tuples så det blir en lista som ser ut så här [[bildnamn,datum],[bildnamn,datum]] så om jag skulle vilja komma åt endast datumdelen blir det strul iaf.
Har åtminstone fått anledning att labba lite i Python :) Här sorteras en lista med namn/heltal-par efter heltal och ger namnen:

Kod: Markera allt

a = [("a", 1), ("c", 3), ("b", 2)]
map(lambda v: v[0], sorted(a, key=lambda v: v[1]))

=> ['a', 'b', 'c']
Det är alltid roligt när man får en anledning att göra nåt skoj :)
Jag ska kolla lite mer på min kod och det här och se hur jag vill göra.
Jag har lite mycket just nu så jag springer lite emellan när jag har tid...
och jag har ändrat en del i koden så den ser lite annorlunda ut jämfört med vad den gör här. Grundprincipen är dock kvar, det jag har lagt till är lite mer felhantering då koden jag la upp här hade en förmåga att krascha vid vissa tillfällen. :)
Substrata skrev: Vet dock inte riktigt vad du ska ha en dictionary till. Ur manualen: There are six sequence types: strings, Unicode strings, lists, tuples, buffers, and xrange objects. Dictionary verkar alltså inte vara en sekvenstyp, som sort() och sorted() är definierad över. Faktum är att sorted(dict, key=f) beter sig som sorted(dict.keys, key=f) när den skickar argumentet till f:

Kod: Markera allt

>>> def p(*t):
...     print t
...     True
... 
>>> a = {"a": 1, "c": 3, "b": 2}
>>> sorted(a, key=p)
('a',)
('c',)
('b',)
['a', 'c', 'b'] # returvärdet...
Så det ser inte ut som en bra lösning att använda dictionaries i det här fallet. Bättre i så fall att använda en lista med tuples, eller en lista med objekt om du vill knyta mer avancerad logik till bild/datum-representeringen.
Anledningen till att jag använder dictionaris är helt enkelt att jag misslyckades med att använda en två dimensionella array som var min grundtanke. Jag fick hela tiden felmeddelande att det inte var en integer när jag la in sökvägen till bilden och visste inte vad jag skulle göra. Och så fick jag ett tips om att dictionarys använder key/value och tyckte det påminde lite om en två dimensionel array dock med den stora nackdelen att den är osorterbar i grunden. Men det gick ju att komma runt ganska enkelt.

Jag ska testa det du har föreslagit men jag vet inte när det blir av, jag har en del annat jag måste göra först, tyvärr. :)

Re: Sorteringsproblem i Python

Postat: 02 maj 2010, 17:39
av BadOmen
Här är den kompletta koden jag har nu ifall nån vill testa den. Den kanske inte är den vackraste och mest effektiva men den ser ut att funka :)
Jag använder fortfarande en dictionary, jag har inte hunnit/idits testa nåt annat än :)

Kod: Markera allt

#!/usr/bin/env python
# usage: skript.py -d directory -s startdate -e enddate

# startdate and enddate must be like 20100101
# startdate and enddate are optional. 
# If just -s (startdate) is used then "all" images from startdate to current date will be found. 
# if just -e (enddate) is used then "all" images that are taken before enddate will be found.
# if just -d is used then all images up to current date will be found.
# -d must be set or it will not work at all.
# There can be images with not supported or missing exif data that will be missed.
# Only jpg files are searched for.

import os
from PIL import Image
from optparse import OptionParser
import time
import sys

#print 'ARGV      :', sys.argv[1:]
parser = OptionParser()
parser.add_option('-d', '--directory', 
                  dest="imgDirectory", 
                  default="not_set",
                  type="string",
                  )
parser.add_option('-s', '--start-date',
                  dest="startDate",
                  default=0,
                  type="int",
                  )
parser.add_option('-e','--end-date',
                  dest="endDate",
                  default=time.strftime("%Y/%m/%d", time.localtime()).replace("/", ""),
                  type="int",
                  )
options, remainder = parser.parse_args()
print " directory	", options.imgDirectory
print " startDate	", options.startDate
print " endDate	", options.endDate
print " This can take a while depending on how many files there is in the directory and its subdirectories."
print "" # makes an emty line


def sort_by_value(d):
    """ Returns the keys of dictionary d sorted by their values """
    items=d.items()
    backitems=[ [v[1],v[0]] for v in items]
    backitems.sort()
    return [ backitems[i][1] for i in range(0,len(backitems))]


def isinteger(x):
  try:
    return int(x)
  except:
    return False

	
if os.path.isdir(options.imgDirectory):
		
	dicImg= {}
	EXTENSIONS = ('.jpg', '.JPG')
	for root, dirs, files in os.walk(options.imgDirectory):
		for name in files:
			os.path.join(root, name)
			
			for ext in EXTENSIONS:
				if name.endswith(ext):
					try:
						im=Image.open(os.path.join(root, name))
					
						try:
							exifdata = im._getexif()
							if hasattr(im, '_getexif'):
								orgtimeTaken = exifdata[0x9003]
								timeTaken = orgtimeTaken[0:10].replace(":","") # takes the 10 first characters and removes the : from 2010:05:02
								timeTaken = timeTaken[0:10].replace(".","")
								timeTaken = isinteger(timeTaken)
								if timeTaken != False and timeTaken >= options.startDate and timeTaken <= options.endDate:
									dicImg[(os.path.join(root, name))]= orgtimeTaken # makes an dictionary with the image path name and name as Key and the time the image was taken as Value
						except TypeError:
							pass
						except KeyError:
							pass
						except AttributeError:
							pass
						except IOError:
							pass
						except:
							print("Something went wrong"),sys.exc_info()
							pass
					except IOError:
						pass
					except:
						print("Something went wrong"),sys.exc_info()
						pass
						
	arrImgName = sort_by_value(dicImg) # Returns the keys of dictionary dicImg sorted by their values i.e. by date and time.

	x=0
	while x < len(arrImgName):
		# because arrImgName has the keys for dicImg sorted by date time I can use these keys in dicImg to get the right image printed out.		
		print arrImgName[x], dicImg[arrImgName[x]] 
		x=x+1
	
	print len(dicImg)," images found."



else:
	print ("You have to specify a directory. -d path/to/images")