Accueil > Projet Made by Sp@r0, Synology > Cadre photo numérique sur syno : MAJ

Cadre photo numérique sur syno : MAJ

11/01/2011

Voici une démo du programme en question, avec quelques remarques :

- Merci à ma fille pour les bruits d’ambiance et la photo

- Merci à ma cuisine

- Le changement de photo est lent mais mon DS110j fait ce qu’il peut pour redimensionné les photos de mon reflex …

You need to install or upgrade Flash Player to view this content, install or upgrade by clicking here.

Voilà je vous expose le concept, sur mon syno je dispose des informations suivantes :
- des photos
- ma domotique (températures, hygrométries, conso EDF, pression atmosphérique)
- une connexion internet pour récupérer plein d’autres infos

Mais je trouve un peu pénible de ne pas avoir d’afficheur qui m’affiche cela directement sans passez par un navigateur internet, au début je voulais utilisez un mini écran supportant la technologie DisplayLink (genre le Mino 720S) mais ces produits sont difficiles à trouver et surtout très chère !!!

Après pas mal de recherche j’ai finalement sélectionner ce produit :
Image IPB
Ce cadre photo numériques 8″ coûte 80€ (SAMSUNG SPF87H) , mais je l’ai acheter d’occasion 40€. Il existe également un modèle 10″(SAMSUNG SPF107H mais il nécessite une alimentation externe). Cette gamme de produit implante une fonction « mini-monitor » qui permet normalement d’utiliser le cadre photo numérique comme un écran usb (un peu comme la technologie displayLink mais en lowcost)

Je me suis grandement inspiré de ce projet mais j’ai préféré le traduire et l’adapter pour que cela tourne sous python (du coup mon code est compatible avec tous synos disposant d’IPKG) (module pyUSB + python 2.5)

Donc ce que je parviens à faire :
- Achat du produit OK
- Protocole USB OK (je vous conseil Bus Hound pas trés connu mais vraiment super mieux que usbsniff)
- Codage en python pour envoyer une image fixe OK (enfin presque reste quelques petits bugs)
- Affichage des photos de la photostation(en fait je n’utilise pas la photostation juste le répertoire) OK
- Affichage de la domotique En cours


Le fonctionnement :
- démarrer le cadre
- activer l’usb (mini monitor ou mode donnée le programme changera le mode au besoin)
- au démarrage le programme (random_img_02.py) liste récursivement toutes les images d’un répertoire (et de ces sous répertoires)
- toutes les 30 secondes on sélectionne une image au hazard on lit les paramétres exifs pour l’orientation de la photo puis on la redimensionne en gardant l’aspect ratio
- envoi de l’image au cadre

En aménagement le programme avec des try except il sera facile de faire un programme qui permet d’éteindre et de rallumer le cadre !!!

Partie 1 : Le pilotage du cadre : « spf_02.py »

?View Code PYTHON
#!/usr/bin/env python2.5
# -*- coding: utf-8 -*-
#
#	Programme de gestion du cadre photo numérique
#	Permet de gerer un SPF-87H
#
 
# Import des librairies necessaires
import sys
import os
import time
import struct
import usb
from random_img_02 import *	
MAXBUFFER = 0xffff
 
# Cherche les peropheriques usb
def Cherche_USB(V_vendor, V_product):
	busses = usb.busses()
	for bus in busses:
		for dev in bus.devices:
			if dev.idVendor == V_vendor and dev.idProduct == V_product:
				print "Trouvez : " + hex(dev.idVendor) + " \ " + hex(dev.idProduct)
				return dev
	print "Nada rien vue ..."
	return 0
 
# Basculement en mode mini-monitor
def Changement_MODE(dev):
	# Une petite verif
	assert dev.idVendor == 0x04E8
	assert dev.idProduct == 0x2033
	# Init
	conf = dev.configurations[0]
	iface = conf.interfaces[0][0]
	# On demande l'ouverture du peripherique
	handle = dev.open()
	handle.detachKernelDriver(iface.interfaceNumber)
	handle.setConfiguration(conf)
	handle.claimInterface(iface)
	handle.setAltInterface(iface)
 
	# Quelques preparatifs
	res = handle.getString(dev.iManufacturer,256)
	print  "usb_get_string_simple : " + res + " \n"
 
	S_trame = [0] * 254
 
	# On envoi la trame magique qui va planter mais on le sait
	try:
		res = handle.controlMsg(usb.TYPE_STANDARD | usb.ENDPOINT_IN, usb.REQ_GET_DESCRIPTOR, S_trame, 0xfe, 0xfe, 1000)
	except:
		print "Changement de mode OK"
 
# Init du mode mini-monitor
def Init_IMAGE(dev):
	# Une petite verif
	assert dev.idVendor == 0x04E8
	assert dev.idProduct == 0x2034
	# Init
	conf = dev.configurations[0]
	iface = conf.interfaces[0][0]
	endpoint = iface.endpoints[1]
	# On demande l'ouverture du peripherique
	handle = dev.open()
	handle.reset()
 
	handle.setConfiguration(1)
	handle.claimInterface(0)
 
	# Quelques preparatifs
	res = handle.getString(dev.iManufacturer,256)
	print  "usb_get_string_simple : " + res + " \n"
 
	for V_i in range(3):
		print "Endpoint:",hex(iface.endpoints[V_i].address)
		print handle.resetEndpoint(iface.endpoints[V_i].address)
		print handle.clearHalt(iface.endpoints[V_i].address)
 
	return handle
 
 
# Chargement d'une image
def Changement_IMAGE(handle, Liste_Fichier):
	S_image = Gen_Image(TrouvePhoto(Liste_Fichier))
	L_trame = len(S_image) + 12
	print hex(L_trame)
	S_trame = '\xa5' + '\x5a' + '\x09' + '\x04' + chr((L_trame%65536)%256) + chr((L_trame%65536)/256) + chr((L_trame/65536)%256) + chr((L_trame/65536)/256) + '\x46' + '\x00' + '\x00' + '\x00'
	print hex(ord(S_trame[4]))
	print hex(ord(S_trame[5]))
	print len(S_trame)
	V_buf = S_trame + S_image
	print " Longeur init + fichier : %d" % len(V_buf)
	if len(V_buf)<=MAXBUFFER:
		V_buffer = V_buf.ljust(MAXBUFFER, '\x00');
		print " Longeur buffer : %d" % len(V_buffer)
		res = handle.bulkWrite(0x2,V_buffer,1000)
		reqBuffer = '\x00'
		res = handle.controlMsg(0xc0, 0x6, reqBuffer, value = 0, index = 0, timeout = 100)
	else:
		V_i = 0
		while V_i < len(V_buf):
			V_bufferA = V_buf[V_i:(V_i + MAXBUFFER)]
			V_i = V_i + MAXBUFFER
			print " envoi morceau : %d" % len(V_bufferA)
			V_buffer = V_bufferA.ljust(MAXBUFFER, '\x00');
			res = handle.bulkWrite(0x2,V_buffer,1000)
		reqBuffer = '\x00'
		res = handle.controlMsg(0xc0, 0x6, reqBuffer, value = 0, index = 0, timeout = 100)
 
	# test control transfert
	#res = handle.ctrltransfer(0xc0, 0x6, 0, 0, 0x2)
	#reqBuffer = '\x00'
	#res = handle.controlMsg(0xc0, 0x6, reqBuffer, value = 0, index = 0, timeout = 100)
 
def Changement_INIT(handle, f_im):
	S_image = f_im.read()
	L_trame = len(S_image) + 12
	print hex(L_trame)
	S_trame = '\xa5' + '\x5a' + '\x09' + '\x04' + chr((L_trame%65536)%256) + chr((L_trame%65536)/256) + chr((L_trame/65536)%256) + chr((L_trame/65536)/256) + '\x46' + '\x00' + '\x00' + '\x00'
	print hex(ord(S_trame[4]))
	print hex(ord(S_trame[5]))
	print len(S_trame)
	V_buf = S_trame + S_image
	print " Longeur init + fichier : %d" % len(V_buf)
	if len(V_buf)<=MAXBUFFER:
		V_buffer = V_buf.ljust(MAXBUFFER, '\x00');
		print " Longeur buffer : %d" % len(V_buffer)
		res = handle.bulkWrite(0x2,V_buffer,1000)
		reqBuffer = '\x00'
		res = handle.controlMsg(0xc0, 0x6, reqBuffer, value = 0, index = 0, timeout = 100)
	else:
		V_i = 0
		while V_i < len(V_buf):
			V_bufferA = V_buf[V_i:(V_i + MAXBUFFER)]
			V_i = V_i + MAXBUFFER
			print " envoi morceau : %d" % len(V_bufferA)
			V_buffer = V_bufferA.ljust(MAXBUFFER, '\x00');
			res = handle.bulkWrite(0x2,V_buffer,1000)
		reqBuffer = '\x00'
		res = handle.controlMsg(0xc0, 0x6, reqBuffer, value = 0, index = 0, timeout = 100)
 
 
# Boucle principal on a vue plus elegant mais cela fontionne
while 1:
	# SPF en mode stockage de masse
	dev = Cherche_USB(0x04E8, 0x2033)
	if dev != 0:
		print "Passage en mode mini-monitor"
		Changement_MODE(dev)
 
	# SPF en mode mini-monitor
	rdev = Cherche_USB(0x04E8, 0x2034)
	if rdev != 0:
		print "Lancement de l'envoi des images"
		handle = Init_IMAGE(rdev)
		f_image = open('domotique.jpg', 'r')
		Changement_INIT(handle, f_image)
		f_image.close()
		Liste_Fichier = []
		Liste_Images(Origine_dir, Liste_Fichier)
		handle = Init_IMAGE(rdev)
		while 1:
			print "Chargement image"
			Changement_IMAGE(handle, Liste_Fichier)
			time.sleep(60)
	time.sleep(1)

Partie 2 : Choix d’une photo : « random_img_02.py »

?View Code PYTHON
#!/usr/bin/env python2.5
# -*- coding: utf-8 -*-
#
#	Ce programme sélectionne une image au hazard dans le repertoire Origine_dir
#	Les images sont redimensionné en 800*480 pour le cadre
#
 
# Import de quelques librairies
from os import listdir
import os
from random import choice
import Image
import EXIF
import StringIO
 
# Definition de quelques variables
ext2conttype = {"jpg": "image/jpeg",
				"jpeg": "image/jpeg"}
Origine_dir = "/volume1/SauvegardeZ/Gros_Fixe/Images/"
 
# Extraction du type MIME
def content_type(filename):
	return ext2conttype[filename[filename.rfind(".")+1:].lower()]
 
# Vérification si le fichier est une image
def isimage(filename):
	filename = filename.lower()
	return filename[filename.rfind(".")+1:] in ext2conttype
 
# Fonction d'extraction de la liste des images
def Liste_Images(Repertoire, Liste_Fichier):
	# Liste elements du repertoire
	for d in listdir(Repertoire):
		if os.path.isdir(Repertoire + d):
			Liste_Images(Repertoire + d + "/", Liste_Fichier)
		if isimage(Repertoire + d):
			Liste_Fichier.append(Repertoire + d)
	return
 
# Recherche d'une photo au hazard
def TrouvePhoto(Liste_Fichier):
	V_Image = choice(Liste_Fichier)
	print V_Image
	print len(Liste_Fichier)
	return V_Image
 
# Genération d'un buffer d'image pour le cadre
def Gen_Image(V_Image):
	# Création d'un buffer temporaire
	buf= StringIO.StringIO()
	# Extraction des infos EXIF
	f = open(V_Image, 'rb')
	tags = EXIF.process_file(f)
	f.close()
	# Ouverture de l'image pour redimensionnement
	im0 = Image.open(V_Image)
	# Détermination de l'orientation
	try:
		Orientation = str(tags['Image Orientation'])
	except:
		Orientation = "Horizontal (normal)"
	print Orientation
	print len(Orientation)
	# Retournement dans l'image si nécessaire
	if Orientation == 'Rotated 90 CW':
		im1 = im0.transpose(Image.ROTATE_270)
	else:
		im1 = im0.copy()
	# Déformation en fonction de l'aspect ratio et de l'orientation
	if (Orientation == 'Rotated 90 CW') | (im1.size[1] > im1.size[0]):
		print "image vertical"
		width = 800
		height = 800 * im1.size[1] / im1.size[0]
		print height
		V_ecart = (height - 480)/2
		print V_ecart
		box = (0, V_ecart, 800, 480+V_ecart)
		im2 = im1.resize((width, height), Image.BILINEAR) # best down-sizing filter
		im3 = im2.crop(box)
	else:
		if (im1.size[0] / im1.size[1]) <= 1.667:
			print "Horizontal 1"
			width = 800
			if im1.size[1] > im1.size[0]:
				height = 800 * im1.size[0] / im1.size[1]
			else:
				height = 800 * im1.size[1] / im1.size[0]			
			print height
			V_ecart = (height - 480)/2
			print V_ecart
			box = (0, V_ecart, 800, 480+V_ecart)
			im2 = im1.resize((width, height), Image.BILINEAR) # best down-sizing filter
			im3 = im2.crop(box)
		else:
			print "Horizontal 2"
			height = 480
			if im1.size[1] > im1.size[0]:
				width = 480 * im1.size[0] / im1.size[1]
			else:
				width = 480 * im1.size[1] / im1.size[0]
			print width
			V_ecart = (height - 800)/2
			print V_ecart
			box = (V_ecart, 0, 800+V_ecart, 480)
			im2 = im1.resize((width, height), Image.BILINEAR) # best down-sizing filter
			im3 = im2.crop(box)
	#im3.save('temp.jpg')
	im3.save(buf, format= 'JPEG')
	return buf.getvalue()
 
#
# Pour test 
#
#Liste_Fichier = []
#Liste_Images(Origine_dir, Liste_Fichier)
#Gen_Image(TrouvePhoto(Liste_Fichier))
Categories: Projet Made by Sp@r0, Synology Tags:
Les commentaires sont fermés.