'''
ImageMac.py by Trocca Riccardo (rtrocca@libero.it)
This module provides functions to display images and Numeric arrays
It provides two classes ImageMacWin e NumericMacWin and two simple methods showImage and
showNumeric.

They work like this:
showImage(Image,"optional window title",zoomFactor)
the same for showNumeric
zoomfactor (defaults to 1) allows to zoom in the image by a factor of 1x 2x 3x and so on
I did't try with a 0.5x or similar.
The windows don't provide a scrollbar or a resize box.
Probably a better solution (and more similar to the original implementation in PIL and NumPy)
would be to save a temp file is some suitable format and then make an application (through appleevents) to open it.
Good guesses should be GraphicConverter or PictureViewer.

However the classes ImageMacWin e NumericMacWin use an extended version of PixMapWrapper in order to 
provide an image buffer and then blit it in the window.

Being one of my first experiences with Python I didn't use Exceptions to signal error conditions, sorry.

'''
import W
from Carbon import Qd
from ExtPixMapWrapper import *
from Numeric import *
import Image
import macfs

class ImageMacWin(W.Window):
	
	def __init__(self,size=(300,300),title="ImageMacWin"):
		self.pm=ExtPixMapWrapper()
		self.empty=1
		self.size=size
		W.Window.__init__(self,size,title)
	
	def Show(self,image,resize=0):
		#print "format: ", image.format," size: ",image.size," mode: ",image.mode
		#print "string len :",len(image.tostring())
		self.pm.fromImage(image)
		self.empty=0
		if resize:
			self.size=(image.size[0]*resize,image.size[1]*resize)
			W.Window.do_resize(self,self.size[0],self.size[1],self.wid)
		self.do_drawing()
	
	def do_drawing(self):
		#print "do_drawing"
		self.SetPort()
		Qd.RGBForeColor( (0,0,0) )
		Qd.RGBBackColor((65535, 65535, 65535))
		Qd.EraseRect((0,0,self.size[0],self.size[1]))
		if not self.empty:
			#print "should blit"
			self.pm.blit(0,0,self.size[0],self.size[1])
		
	def do_update(self,macoswindowid,event):
		#print "update"
		self.do_drawing()
		
class NumericMacWin(W.Window):
	
	def __init__(self,size=(300,300),title="ImageMacWin"):
		self.pm=ExtPixMapWrapper()
		self.empty=1
		self.size=size
		W.Window.__init__(self,size,title)
	
	def Show(self,num,resize=0):
		#print "shape: ", num.shape
		#print "string len :",len(num.tostring())
		self.pm.fromNumeric(num)
		self.empty=0
		if resize:
			self.size=(num.shape[1]*resize,num.shape[0]*resize)
			W.Window.do_resize(self,self.size[0],self.size[1],self.wid)
		self.do_drawing()
	
	def do_drawing(self):
		#print "do_drawing"
		self.SetPort()
		Qd.RGBForeColor( (0,0,0) )
		Qd.RGBBackColor((65535, 65535, 65535))
		Qd.EraseRect((0,0,self.size[0],self.size[1]))
		if not self.empty:
			#print "should blit"
			self.pm.blit(0,0,self.size[0],self.size[1])
		
	def do_update(self,macoswindowid,event):
		#print "update"
		self.do_drawing()

'''
Some utilities: convert an Image to a NumPy array and viceversa.
The Image2Numeric function doesn't make any color space conversion.
The Numeric2Image function returns an L or RGB or RGBA images depending on the shape of
the array:
	(x,y)	-> 	'L'
	(x,y,1)	-> 	'L'
	(x,y,3)	->	'RGB'
	(x,y,4)	->	'RGBA'
'''
def Image2Numeric(im):
	tmp=fromstring(im.tostring(),UnsignedInt8)
	
	if (im.mode=='RGB')|(im.mode=='YCbCr'):
		bands=3
	
	if (im.mode=='RGBA')|(im.mode=='CMYK'):
		bands=4
	
	if (im.mode=='L'):
		bands=1

	tmp.shape=(im.size[0],im.size[1],bands)
	return transpose(tmp,(1,0,2))

def Numeric2Image(num):
	#sometimes a monoband image's shape can be (x,y,1), other times just (x,y). Here w deal with both
	if len(num.shape)==3: 
		bands=num.shape[2]
		if bands==1:
			mode='L'
		elif bands==3:
			mode='RGB'
		else:
			mode='RGBA'
		return Image.fromstring(mode,(num.shape[1],num.shape[0]),transpose(num,(1,0,2)).astype(UnsignedInt8).tostring())
	else:
		return Image.fromstring('L',(num.shape[1],num.shape[0]),transpose(num).astype(UnsignedInt8).tostring())	

def showImage(im,title="ImageWin",zoomFactor=1):
	imw=ImageMacWin((300,200),title)
	imw.open()
	try:
		imw.Show(im,zoomFactor )
	except MemoryError,e:
		imw.close()
		print "ImageMac.showImage: Insufficient Memory"
		

def showNumeric(num,title="NumericWin",zoomFactor=1):
	#im=Numeric2Image(num)
	numw=NumericMacWin((300,200),title)
	numw.open()
	try:
		numw.Show(num,zoomFactor )
	except MemoryError:
		numw.close()
		print "ImageMac.showNumeric Insufficient Memory"
	
'''
GimmeImage pops up a file dialog and asks for an image file.
it returns a PIL image.
Optional argument: a string to be displayed by the dialog. 
'''
	
def GimmeImage(prompt="Image File:"):
	import macfs
	fsspec, ok = macfs.PromptGetFile(prompt)
	if ok:
		path = fsspec.as_pathname()
		return Image.open(path)
	return None
	
'''
This is just some experimental stuff:
	Filter3x3 a convolution filter (too slow use signal tools instead)
	diffBWImage subtracts 2 images contained in NumPy arrays
	averageN it computes the average of a list incrementally
	BWImage converts an RGB or RGBA image (in a NumPy array) to BW
	SplitBands splits the bands of an Image (inside a NumPy)
	NumHisto and PlotHisto are some experiments to plot an intesity histogram
'''

def Filter3x3(mul,fi,num):
	(a,b,c,d,e,f,g,h,i)=fi
	print fi
	num.shape=(num.shape[0],num.shape[1])
	res=zeros(num.shape)
	for x in range(1,num.shape[0]-1):
		for y in range(1,num.shape[1]-1):
			xb=x-1
			xa=x+1
			yb=y-1
			ya=y+1
			res[x,y]=int((a*num[xb,yb]+b*num[x,yb]+c*num[xa,yb]+d*num[xb,y]+e*num[x,y]+f*num[xa,y]+g*num[xb,ya]+h*num[x,ya]+i*num[xa,ya])/mul)
	return res
		
def diffBWImage(num1,num2):
	return 127+(num1-num2)/2

def averageN(N,avrg,new):
	return ((N-1)*avrg+new)/N
		
def BWImage(num):
	if num.shape[2]==3:
		bw=array(((0.3086,0.6094,0.0820)))
	else:
		bw=array(((0.3086,0.6094,0.0820,0)))
	res=innerproduct(num,bw)
	res.shape=(res.shape[0],res.shape[1])
	return res

def SplitBands(num):
	x=num.shape[0]
	y=num.shape[1]
	if num.shape[2]==3:
		return (reshape(num[:,:,0],(x,y)),reshape(num[:,:,1],(x,y)),reshape(num[:,:,2],(x,y)))
	else:
		return (reshape(num[:,:,0],(x,y)),reshape(num[:,:,1],(x,y)),reshape(num[:,:,2],(x,y)),reshape(num[:,:,3],(x,y)))

def NumHisto(datas):
	#print "type(datas) ",type(datas)
	a=ravel(datas)
	n=searchsorted(sort(a),arange(0,256))
	n=concatenate([n,[len(a)]])
	return n[1:]-n[:-1]
	
def PlotHisto(datas,ratio=1):
	from graphite import *
	from MLab import max
	h=NumHisto(datas)
	#print "histo: ",h
	#print "histo.shape: ",h.shape
	maxval=max(h)
	#print "maxval ",maxval
	h.shape=(256,1)
	x=arange(0,256)
	x.shape=(256,1)
	datah=concatenate([x,h],1)
	print "data: "
	print datah
	g=Graph()
	g.datasets.append(Dataset(datah))
	f0=PointPlot()
	f0.lineStyle = LineStyle(width=2, color=red, kind=SOLID)
	g.formats = [f0]
	g.axes[X].range = [0,255]
	g.axes[X].tickMarks[0].spacing = 10
	#g.axes[X].tickMarks[0].labels = "%d"
	g.axes[Y].range = [0,maxval/ratio]
	g.bottom = 370
	g.top =10
	g.left=10
	g.right=590
	
	genOutput(g,'QD',size=(600,400))
	
def test():
	import MacOS
	import Image
	import ImageFilter
	import Numeric
	fsspec, ok = macfs.PromptGetFile("Image File:")
	if ok: 
		path = fsspec.as_pathname()
		im=Image.open(path)
		#im2=im.filter(ImageFilter.SMOOTH)
		showImage(im,"normal")
		num=Image2Numeric(im)
		#num=Numeric.transpose(num,(1,0,2))
		
		showNumeric(num,"Numeric")
		
		print "num.shape ",num.shape
		showImage(Numeric2Image(num),"difficile")
		#showImage(im.filter(ImageFilter.SMOOTH),"smooth")
		#showImage(im.filter(ImageFilter.FIND_EDGES).filter(ImageFilter.SHARPEN),"detail")
	
		print "here"
	else:
		print "did not open file"

if __name__ == '__main__':
	test()