and changes the alternate colorspace's "ramp" slope and offset. The program only does linear adjustments.
I've been trying to merge PIL and wxPython and since have come up with a better way to display the results,
but I think you want only the processing algorithms, anyway. I don't really can't swear to the accuracy
of the colorspace conversions other than they convert from RGB and then back again perfectly. I was able
to correct several apparent bugs in the algorithms. I don't know where to find a "definitive" source for the algorithms,
much less an explanation for how they work.
I'd appreciate any comments/suggestions you may have. I intend to rewrite the app's GUI and
would also appreciate finding a more useful mapping function than a linear-ramp to linear-ramp function.
Ray Pasco
import wx #----------------------------
def Pil2Bitmap (imPil):
wxImage = Pil2WxImage (imPil)
bitmap = WxImage2Bitmap (wxImage)
return bitmap
#end def
def Pil2WxImage (imPil):
wxImage = wx.EmptyImage (imPil.size[0], imPil.size[1])
wxImage.SetData (imPil.convert('RGB').tostring())
return wxImage
#end def
def WxImage2Bitmap (image):
bitmap = image.ConvertToBitmap()
return bitmap
#end def
#--------
def Bitmap2Pil (bitmap):
WxImage = Bitmap2WxImage (bitmap)
imPil = WxImage2Pil (WxImage)
return imPil
#end def
def Bitmap2WxImage (bitmap):
wximage = wx.ImageFromBitmap (bitmap)
return wximage
#end def
def WxImage2Pil (wxImage):
imPil = Image.new ('RGB', (wxImage.GetWidth(), wxImage.GetHeight()))
imPil.fromstring (wxImage.GetData())
return imPil
#end def
#----------------------------
## Colorspace conversions library
import sys
def Rgb2Cmyk (rgbtuple):
def Rgb2Cmy (rgbtuple):
# rgb tuple values are in [0 ... 255]
r, g, b = rgbtuple[0], rgbtuple[1], rgbtuple[2],
c = 1 - (r / 255.0)
m = 1 - (g / 255.0)
y = 1 - (b / 255.0)
return c, m, y
#end def Rgb2Cmy
def Cmy2Cmyk (cmytuple):
# cmy tuple values are in [0.0 ... 1.0]
c, m, y = cmytuple[0], cmytuple[1], cmytuple[2]
if (c == 1.0) and (m == 1.0) and (y == 1.0):
K = 1.0 # special dispensation for absolute black to avoid
division-by-zero
C = 0.0
M = 0.0
Y = 0.0
else:
K = min (1.0, c, m, y)
C = (c - K) / (1.0 - K)
M = (m - K) / (1.0 - K)
Y = (y - K) / (1.0 - K)
#end if
return C, M, Y, K
#end def Cmy2Cmyk
#------------------------
# rgb tuple values are in [0...255]
cmytuple = Rgb2Cmy (rgbtuple)
C, M, Y, K = Cmy2Cmyk (cmytuple)
return C, M, Y, K
#end def Rgb2Cmyk
def Cmyk2Rgb (cmyktuple):
def Cmyk2Cmy (cmyktuple):
# CMYK tuple values are in [0.0 ... 1.0]
C, M, Y, K = cmyktuple[0], cmyktuple[1], cmyktuple[2], cmyktuple[3]
c = ((C * (1.0 - K)) + K)
m = ((M * (1.0 - K)) + K)
y = ((Y * (1.0 - K)) + K)
return c, m, y
#end def Cmyk2Cmy
def Cmy2Rgb (cmytuple):
# cmy tuple values are in [0.0 .. 1.0]
c, m, y = cmytuple[0], cmytuple[1], cmytuple[2]
r = int (((1.0 - c) * 255.0) + 0.5)
g = int (((1.0 - m) * 255.0) + 0.5)
b = int (((1.0 - y) * 255.0) + 0.5)
return r, g, b
#end def Cmy2Rgb
#------------------------
C, M, Y, K = cmyktuple
# Make sure that CMYK values are be in [0.0 ... 1.0]
C = min (max (0.0, C), 1.0)
M = min (max (0.0, M), 1.0)
Y = min (max (0.0, Y), 1.0)
K = min (max (0.0, K), 1.0)
cmytuple = Cmyk2Cmy (cmyktuple)
r, g, b = Cmy2Rgb (cmytuple)
return r, g, b
#end def Cmyk2Rgb
def Rgb2Hsl (rgbtuple):
# H, S, L will be in [0.0 ... 1.0]
r, g, b = rgbtuple
rScaled = r/255.0
gScaled = g/255.0
bScaled = b/255.0
rgbMin = min (rScaled, gScaled, bScaled) # Min RGB value
rgbMax = max (rScaled, gScaled, bScaled) # Max RGB value
deltaRgb = rgbMax - rgbMin # Delta RGB value
L = (rgbMax + rgbMin) / 2.0
if (deltaRgb == 0.0): # This is a gray, no chroma.
H = 0.0
S = 0.0 # Done !
else: # Chromatic data...
if (L < 0.5):
S = deltaRgb / (rgbMax + rgbMin)
else:
S = deltaRgb / (2.0 - rgbMax - rgbMin)
#end if
deltaR = (((rgbMax - rScaled)/6.0) + (deltaRgb/2.0)) / deltaRgb
deltaG = (((rgbMax - gScaled)/6.0) + (deltaRgb/2.0)) / deltaRgb
deltaB = (((rgbMax - bScaled)/6.0) + (deltaRgb/2.0)) / deltaRgb
if (rScaled == rgbMax):
H = deltaB - deltaG
elif (gScaled == rgbMax):
H = (1.0/3.0) + deltaR - deltaB
elif (bScaled == rgbMax):
H = (2.0/3.0) + deltaG - deltaR
#end if
H = H % 1.0
#end if
return (H, S, L)
#end def Rgb2Hsl
def Hsl2Rgb (hsltuple):
def HslHue2Rgb (v1, v2, vH):
if (vH < 0.0): vH += 1.0
if (vH > 1.0): vH -= 1.0
if ((6.0 * vH) < 1.0): return (v1 + (v2 - v1) * 6.0 * vH)
if ((2.0 * vH) < 1.0): return (v2)
if ((3.0 * vH) < 2.0): return (v1 + (v2 - v1) * ((2.0/3.0) - vH) *
6.0)
return v1
#end def HslHue2Rgb
#------------------------
# H, S, L in [0.0 .. 1.0]
H, S, L = hsltuple
if (S == 0.0): # RGB grayscale
r = L * 255.0 # R, G, B in [0 .. 255]
g = L * 255.0
b = L * 255.0
else:
if (L < 0.5):
var_2 = L * (1.0 + S)
else:
var_2 = (L + S) - (S * L)
#end if
var_1 = (2.0 * L) - var_2
r = 255 * HslHue2Rgb (var_1, var_2, H + (1.0/3.0))
g = 255 * HslHue2Rgb (var_1, var_2, H )
b = 255 * HslHue2Rgb (var_1, var_2, H - (1.0/3.0))
#end if
r = int (r + 0.5)
g = int (g + 0.5)
b = int (b + 0.5)
return (r, g, b)
#end def Hsl2Rgb
def Rgb2Hsv (rgbtuple):
# H, S, V will be in [0.0 ... 1.0]
r, g, b = rgbtuple
r = r / 255.0
g = g / 255.0
b = b / 255.0
minRgb = min (r, g, b)
maxRgb = max (r, g, b)
deltaRgb = maxRgb - minRgb
v = maxRgb
if (deltaRgb == 0): # This is a gray, no chroma...
h = 0.0
s = 0.0
else: # Chromatic data
s = deltaRgb / maxRgb
del_R = (((maxRgb - r) / 6.0) + (deltaRgb / 2.0)) / deltaRgb
del_G = (((maxRgb - g) / 6.0) + (deltaRgb / 2.0)) / deltaRgb
del_B = (((maxRgb - b) / 6.0) + (deltaRgb / 2.0)) / deltaRgb
if (r == maxRgb):
h = del_B - del_G
elif (g == maxRgb):
h = (1.0 / 3.0) + del_R - del_B
elif (b == maxRgb):
h = (2.0 / 3.0) + del_G - del_R
#end if
h = h % 1.0
#end if
return (h, s, v)
#end def Rgb2Hsv
def Hsv2Rgb (hsvtuple):
h, s, v = hsvtuple
if (s == 0.0):
r = v * 255.0
g = v * 255.0
b = v * 255.0
else:
h = h * 6.0
I = int (h) # floor function
F = h - I
P = v * (1.0 - s)
Q = v * (1.0 - s * F)
T = v * (1.0 - s * (1.0 - F))
if (I == 4):
r = T
g = P
b = v
elif (I == 5):
r = v
g = P
b = Q
elif (I == 0):
r = v
g = T
b = P
elif (I == 1):
r = Q
g = v
b = P
elif (I == 2):
r = P
g = v
b = T
elif (I == 3):
r = P
g = Q
b = v
#end if
r = int ((r * 255.0) + 0.5)
g = int ((g * 255.0) + 0.5)
b = int ((b * 255.0) + 0.5)
#end if
return (r, g, b)
#end def Hsv2Rgb
#--------------------------------------
# Stand-alone test for colorspaces.py
if __name__ == '__main__':
import Image
if len(sys.argv) < 4:
print
print 'Colorspaces: Values for R, G and B must be given, each in the
range [0..255]'
print
sys.exit (1)
#end if
r = eval (sys.argv [1])
g = eval (sys.argv [2])
b = eval (sys.argv [3])
rgbtuple = (r, g, b)
#--------- HSL --------------------------------------
hsltuple = Rgb2Hsl (rgbtuple)
h, s, l = hsltuple
print
print 'Colorspaces: RGB (%3d, %3d, %3d) ==> HSL (%1.3f, %1.3f, %1.3f)'
% (r, g, b, h, s, l)
print
rPrime, gPrime, bPrime = Hsl2Rgb (hsltuple)
print 'Colorspaces: HSL (%1.3f, %1.3f, %1.3f) ==> RGB (%3d, %3d, %3d)'
% (h, s, l, rPrime, gPrime, bPrime)
print
#--------- CMYK -------------------------------------
cmyktuple = Rgb2Cmyk (rgbtuple)
c, y, m, k = cmyktuple
print
print 'Colorspaces: RGB (%3d, %3d, %3d) ==> CMYK (%1.3f, %1.3f, %1.3f,
%1.3f)' % (r, g, b, c, y, m, k)
print
rPrime, gPrime, bPrime = Cmyk2Rgb (cmyktuple)
print 'Colorspaces: CMYK (%1.3f, %1.3f, %1.3f, %1.3f) ==> RGB (%3d,
%3d, %3d)' % (c, m, y, k, rPrime, gPrime, bPrime)
print
#--------- HSV --------------------------------------
hsvtuple = Rgb2Hsv (rgbtuple)
h, s, v = hsvtuple
print
print 'Colorspaces: RGB (%3d, %3d, %3d) ==> HSV (%1.3f, %1.3f, %1.3f)'
% (r, g, b, h, s, v)
print
rPrime, gPrime, bPrime = Hsv2Rgb (hsvtuple)
print 'Colorspaces: HSL (%1.3f, %1.3f, %1.3f) ==> RGB (%3d, %3d, %3d)'
% (h, s, v, rPrime, gPrime, bPrime)
print
#end if
import wx
import Image
import os, sys
from colorspaces import * # pil <--> hsl, hsv, cmyk
from imageconverts import * # pilIm --> bitmap
#----------------------------
import inspect, os
def pressenter (prompt='\npress return...'): raw_input (prompt)
def rangelen(listobj): return xrange(len(listobj))
def showStack():
for stack in inspect.stack(): print stack[2:4]
#end def
exit = os._exit
def delFileExist (filename):
if os.path.exists (filename): os.remove (filename)
#end def
#----------------------------
global filpathname; filepathname = None # create the global instance
global thumbnailXsize; thumbnailXsize = 0 # instantiate
global thumbnailYsize; thumbnailYsize = 0
global colorspaces; colorspaces = ['HSL-', 'HSV-', 'CMYK']
global colorspaceStr; colorspaceStr = colorspaces [0]
global tctrlVar1; tctrlVar1 = None # instantiate
global tctrlVar2; tctrlVar2 = None
global tctrlVar3; tctrlVar3 = None
global tctrlVar4; tctrlVar4 = None
# These default values cause no modifications to be made to the transformed
image's vector space values.
# Thus, when the inverse transform is applied, the resulting image will be
identical
# to the original image.
global x1Var1; x1Var1 = 0.0 # initial Y=0.0 intercept (the X axis
intercept)
global x2Var1; x2Var1 = 1.0 # initial Y=1.0 intercept
global x1Var2; x1Var2 = 0.0
global x2Var2; x2Var2 = 1.0
global x1Var3; x1Var3 = 0.0
global x2Var3; x2Var3 = 1.0
global x1Var4; x1Var4 = 0.0
global x2Var4; x2Var4 = 1.0
class AppFrame (wx.Frame):
def __init__ (self, parent, id, title, position, size):
global filepathname
self.parentFrame = self
self.mainframePsn = position
self.mainframeSize = size
wx.Frame.__init__ (self, parent, id, title, position, size)
# Create the File menu
self.CreateStatusBar() # A StatusBar in the bottom of the window
# Setting up the menu.
filemenu= wx.Menu()
idOpen = wx.NewId()
filemenu.Append (idOpen, "&Open"," Open a Text File")
wx.EVT_MENU (self, idOpen, self.OnOpen)
filemenu.AppendSeparator()
idAbout = wx.NewId()
filemenu.Append (idAbout, "&About"," Information about this program")
wx.EVT_MENU (self, idAbout, self.OnAbout)
filemenu.AppendSeparator()
idExit = wx.NewId()
filemenu.Append (idExit,"E&xit"," Terminate the program")
wx.EVT_MENU (self, idExit, self.OnExit)
# Creating the menubar.
menuBar = wx.MenuBar()
menuBar.Append (filemenu,"&File") # Adding the "filemenu" to the MenuBar
self.SetMenuBar (menuBar) # Adding the MenuBar to the Frame content.
#----------
AppPanel (self, -1, position, size)
self.Show (True) # show self frame
#end def __init__
def OnOpen (self, event): # Open a file
global filepathname
# 'wx.CHANGE_DIR' is necessary to return a full pathname in Win98
dlg = wx.FileDialog (self, "Choose a file",
style=wx.OPEN|wx.FILE_MUST_EXIST|wx.CHANGE_DIR,
wildcard='*.*')
if dlg.ShowModal() == wx.ID_OK:
filename = dlg.GetFilename()
dirname = dlg.GetDirectory()
filepathname = os.path.join (dirname, filename)
#end if
if (type (filepathname) == type ('abc')) or (type (filepathname) ==
type (u'abc')):
try:
self.referenceFrame.Destroy()
except:
pass
#end if
self.ReferenceFrame()
#end if
dlg.Destroy()
#end def OnOpen
def ReferenceFrame (self):
global filepathname
referenceframeXpsn = self.mainframePsn[0] + self.mainframeSize[0]
referenceframepsn = (referenceframeXpsn, self.mainframePsn[1])
referenceframesize = (300, 300) # is also the transformed image
frame's size
self.referenceFrame = wx.Frame (self.parentFrame, -1, "Original Image
", referenceframepsn, referenceframesize)
# Create the reference frame's panel
referencePanel = wx.Panel (self.referenceFrame, -1)
referencePanel.SetBackgroundColour ('#C9E3E5')
# Create the reference bitmap
staticBitmap = wx.StaticBitmap (referencePanel, -1, wx.EmptyBitmap(5,
5))
# Get and resize the original image to fit in the reference panel and
store it to a bitmap
bitmap = GetBitmapFromFile (filepathname, referenceframesize)
staticBitmap.SetBitmap (bitmap)
boxsizer = wx.BoxSizer (wx.VERTICAL)
expand = False
boxsizer.Add (staticBitmap, expand)
referencePanel.SetAutoLayout (True)
referencePanel.SetSizer (boxsizer)
# Show the reference frame
self.referenceFrame.Show (True)
#end def ReferenceFrame
def OnAbout (self, event): # Create a message dialog box
dlg = wx.MessageDialog (self, 'A colorspace manipulation\nprogram in
wxPython',
' About Colorspace Transform', wx.OK)
dlg.ShowModal() # Shows it
dlg.Destroy() # finally destroy it when finished.
#end def
def OnExit (self, event):
self.Close (True) # Close the application.
#end def
#end class AppFrame
class AppPanel (wx.Panel):
def __init__ (self, parentframe, id, mainframePsn, mainframeSize):
global colorspaces
global colorspaceStr
global filepathname
global x1Var1, x2Var1
self.parentFrame = parentframe
self.mainframePsn = mainframePsn
self.mainframeSize = mainframeSize
wx.Panel.__init__ (self, parentframe, id)
self.SetBackgroundColour ('#C9E3E5')
boxsizerV = wx.BoxSizer (wx.VERTICAL) # all panel widgets go in here
expand = False
hgt = 40
padding = 0
#--------------------
rbId = wx.NewId()
numRbRows = 1
# Display the colorspace strings without any trailing dash characters;
# Create a new string list for this purpose.
modcolorspaces = []
for cspaceStr in colorspaces:
if cspaceStr[-1] == '-':
modcolorspaces.append (cspaceStr[:-1]) # up to the dash
char
else:
modcolorspaces.append (cspaceStr[:]) # the wholeoriginal
string
#end if
#end for
radbox = wx.RadioBox (self, rbId, "Color Space", wx.DefaultPosition,
wx.DefaultSize,
modcolorspaces, numRbRows, wx.RA_SPECIFY_ROWS)
radbox.SetBackgroundColour (self.GetBackgroundColour())
radbox.SetBackgroundColour (wx.WHITE)
radbox.SetToolTip (wx.ToolTip ('Select a colorspace model:'))
radbox.SetLabel ('Colorspace Model Selection')
wx.EVT_RADIOBOX (self, rbId, self.EvtRadioBox)
# Apparently the first radiobutton is always selected by default,
# so, adjust the program's setting to reflect this.
colorspaceStr = colorspaces [0]
boxsizerX1X2var1 = self.VarControlsVar1 (self, colorspaceStr[0].upper())
boxsizerX1X2var2 = self.VarControlsVar2 (self, colorspaceStr[1].upper())
boxsizerX1X2var3 = self.VarControlsVar3 (self, colorspaceStr[2].upper())
boxsizerX1X2var4 = self.VarControlsVar4 (self, colorspaceStr[3].upper())
#--------------------
boxsizerV.Add (radbox, expand, wx.CENTER|wx.ALL, 10)
bordersize = 0
boxsizerV.Add (boxsizerX1X2var1, expand, wx.CENTER|wx.ALL, bordersize)
boxsizerV.Add (boxsizerX1X2var2, expand, wx.CENTER|wx.ALL, bordersize)
boxsizerV.Add (boxsizerX1X2var3, expand, wx.CENTER|wx.ALL, bordersize)
boxsizerV.Add (boxsizerX1X2var4, expand, wx.CENTER|wx.ALL, bordersize)
xformbtn = wx.Button (self, -1, "Show Transformed Image")
wx.EVT_BUTTON (self, xformbtn.GetId(), self.xformBtnHandler)
boxsizerV.Add (xformbtn, expand, wx.CENTER|wx.ALL, 10) # a 10-pixel
border all around
self.SetAutoLayout (True)
self.SetSizer (boxsizerV)
self.Show (True)
#end def __init__
def EvtRadioBox (self, event):
global tctrlVar1
global tctrlVar2
global tctrlVar3
global tctrlVar4
colorspaceIndex = event.GetInt()
colorspaceStr = colorspaces [colorspaceIndex]
# Write the colorspace string chars into their text controls.
tctrlVar1.Clear()
tctrlVar1.SetInsertionPoint (0)
tctrlVar1.AppendText (colorspaceStr[0].upper())
tctrlVar2.Clear()
tctrlVar2.SetInsertionPoint (0)
tctrlVar2.AppendText (colorspaceStr[1].upper())
tctrlVar3.Clear()
tctrlVar3.SetInsertionPoint (0)
tctrlVar3.AppendText (colorspaceStr[2].upper())
tctrlVar4.Clear()
tctrlVar4.SetInsertionPoint (0)
tctrlVar4.AppendText (colorspaceStr[3].upper())
#end def
def VarControlsVar1 (self, parentPanel, varLabelChar):
global tctrlVar1
def OnSpinX1 (event):
global x1Var1
x1Var1 = (event.GetPosition() - 200) / 200.0 # raw spinbutton
value
self.textX1Var1.SetValue ('%1.3f' % (x1Var1))
#end def
def OnSpinX2 (event):
global x2Var1
x2Var1 = (event.GetPosition() - 200) / 200.0 # raw spinbutton
value
self.textX2Var1.SetValue ('%1.3f' % (x2Var1))
#end def
def ResetX1BtnHandler (event):
global x1Var1
self.spinX1.SetValue (200) # 0.000
x1Var1 = 0.00
self.textX1Var1.SetValue ('%1.3f' % (x1Var1))
#end def ResetX1BtnHandler
def ResetX2BtnHandler (event):
global x2Var1
self.spinX2.SetValue (400) # 0.000
x2Var1 = 1.00
self.textX2Var1.SetValue ('%1.3f' % (x2Var1))
#end def ResetX1BtnHandler
#--------------------
expand = False
hgt = 40
padding = 0
boxsizerX1X2 = wx.BoxSizer (wx.HORIZONTAL)
boxsizerX1V = wx.BoxSizer (wx.VERTICAL)
labelX1 = wx.StaticText (self, -1, "X1: Y=0.0 Intercept:",
wx.DefaultPosition)
boxsizerX1V.Add (labelX1, expand, wx.CENTER|wx.ALL, padding)
boxsizerX1 = wx.BoxSizer (wx.HORIZONTAL)
self.textX1Var1 = wx.TextCtrl (self, -1, "0.000", wx.DefaultPosition,
wx.Size(60, -1),
style=wx.TE_READONLY)
boxsizerX1.Add (self.textX1Var1, expand, wx.CENTER|wx.ALL, padding)
self.spinX1 = wx.SpinButton (self, 20, wx.Point(0, 0), wx.Size(hgt,
hgt), wx.SP_HORIZONTAL)
self.spinX1.SetRange (0, 600) # -1.000 .. 2.000
self.spinX1.SetValue (200) # x1 = 0.000
wx.EVT_SPIN (self.spinX1, 20, OnSpinX1)
boxsizerX1.Add (self.spinX1, expand, 0, padding)
boxsizerX1V.Add (boxsizerX1, expand, 0, padding)
resetX1btn = wx.Button (self, -1, "Reset to 0.000")
boxsizerX1V.Add (resetX1btn, expand, 0, padding)
wx.EVT_BUTTON (self, resetX1btn.GetId(), ResetX1BtnHandler)
boxsizerX2V = wx.BoxSizer (wx.VERTICAL)
labelX2 = wx.StaticText (self, -1, "X2: Y=1.0 Intercept:",
wx.DefaultPosition)
boxsizerX2V.Add (labelX2, expand, wx.CENTER|wx.ALL, padding)
boxsizerX2 = wx.BoxSizer (wx.HORIZONTAL)
self.textX2Var1 = wx.TextCtrl (self, -1, "1.000", wx.DefaultPosition,
wx.Size(60, -1),
style=wx.TE_READONLY)
boxsizerX2.Add (self.textX2Var1, expand, wx.CENTER|wx.ALL, padding)
self.spinX2 = wx.SpinButton (self, 20, wx.DefaultPosition, wx.Size(hgt,
hgt), wx.SP_HORIZONTAL)
self.spinX2.SetRange (0, 600) # -1.000 .. 2.000
self.spinX2.SetValue (400) # x2 = 1.000
wx.EVT_SPIN (self.spinX2, 20, OnSpinX2)
boxsizerX2.Add (self.spinX2, expand, wx.CENTER|wx.ALL, padding)
boxsizerX2V.Add (boxsizerX2, expand, 0, padding)
resetX2btn = wx.Button (self, -1, "Reset to 1.000")
boxsizerX2V.Add (resetX2btn, expand, 0, padding)
wx.EVT_BUTTON (self, resetX2btn.GetId(), ResetX2BtnHandler)
tctrlVar1 = wx.TextCtrl (self, -1, varLabelChar.upper(),
wx.DefaultPosition, wx.Size(20, -1),
style=wx.TE_READONLY)
boxsizerX1X2.Add (tctrlVar1, expand, wx.CENTER|wx.ALL, 10)
boxsizerX1X2.Add (boxsizerX1V, expand, wx.CENTER|wx.ALL, 10)
boxsizerX1X2.Add (boxsizerX2V, expand, wx.CENTER|wx.ALL, 10)
return boxsizerX1X2
#end def VarControlsVar1
def VarControlsVar2 (self, parentPanel, varLabelChar):
global tctrlVar2
def OnSpinX1 (event):
global x1Var2
x1Var2 = (event.GetPosition() - 200) / 200.0 # raw spinbutton
value
self.textX1Var2.SetValue ('%1.3f' % (x1Var2))
#end def
def OnSpinX2 (event):
global x2Var2
x2Var2 = (event.GetPosition() - 200) / 200.0 # raw spinbutton
value
self.textX2Var2.SetValue ('%1.3f' % (x2Var2))
#end def
def ResetX1BtnHandler (event):
global x1Var2
self.spinX1.SetValue (200) # 0.000
x1Var2 = 0.00
self.textX1Var2.SetValue ('%1.3f' % (x1Var2))
#end def ResetX1BtnHandler
def ResetX2BtnHandler (event):
global x2Var2
self.spinX2.SetValue (400) # 0.000
x2Var2 = 1.00
self.textX2Var2.SetValue ('%1.3f' % (x2Var2))
#end def ResetX1BtnHandler
#--------------------
expand = False
hgt = 40
padding = 0
boxsizerX1X2 = wx.BoxSizer (wx.HORIZONTAL)
boxsizerX1V = wx.BoxSizer (wx.VERTICAL)
labelX1 = wx.StaticText (self, -1, "X1: Y=0.0 Intercept:",
wx.DefaultPosition)
boxsizerX1V.Add (labelX1, expand, wx.CENTER|wx.ALL, padding)
boxsizerX1 = wx.BoxSizer (wx.HORIZONTAL)
self.textX1Var2 = wx.TextCtrl (self, -1, "0.000", wx.DefaultPosition,
wx.Size(60, -1),
style=wx.TE_READONLY)
boxsizerX1.Add (self.textX1Var2, expand, wx.CENTER|wx.ALL, padding)
self.spinX1 = wx.SpinButton (self, 20, wx.DefaultPosition, wx.Size(hgt,
hgt), wx.SP_HORIZONTAL)
self.spinX1.SetRange (0, 600) # -1.000 .. 2.000
self.spinX1.SetValue (200) # x1 = 0.000
wx.EVT_SPIN (self.spinX1, 20, OnSpinX1)
boxsizerX1.Add (self.spinX1, expand, 0, padding)
boxsizerX1V.Add (boxsizerX1, expand, 0, padding)
resetX1btn = wx.Button (self, -1, "Reset to 0.000")
boxsizerX1V.Add (resetX1btn, expand, 0, padding)
wx.EVT_BUTTON (self, resetX1btn.GetId(), ResetX1BtnHandler)
boxsizerX2V = wx.BoxSizer (wx.VERTICAL)
labelX2 = wx.StaticText (self, -1, "X2: Y=1.0 Intercept:",
wx.DefaultPosition)
boxsizerX2V.Add (labelX2, expand, wx.CENTER|wx.ALL, padding)
boxsizerX2 = wx.BoxSizer (wx.HORIZONTAL)
self.textX2Var2 = wx.TextCtrl (self, -1, "1.000", wx.DefaultPosition,
wx.Size(60, -1),
style=wx.TE_READONLY)
boxsizerX2.Add (self.textX2Var2, expand, wx.CENTER|wx.ALL, padding)
self.spinX2 = wx.SpinButton (self, 20, wx.DefaultPosition, wx.Size(hgt,
hgt), wx.SP_HORIZONTAL)
self.spinX2.SetRange (0, 600) # -1.000 .. 2.000
self.spinX2.SetValue (400) # x2 = 1.000
wx.EVT_SPIN (self.spinX2, 20, OnSpinX2)
boxsizerX2.Add (self.spinX2, expand, wx.CENTER|wx.ALL, padding)
boxsizerX2V.Add (boxsizerX2, expand, 0, padding)
resetX2btn = wx.Button (self, -1, "Reset to 1.000")
boxsizerX2V.Add (resetX2btn, expand, 0, padding)
wx.EVT_BUTTON (self, resetX2btn.GetId(), ResetX2BtnHandler)
tctrlVar2 = wx.TextCtrl (self, -1, varLabelChar.upper(),
wx.DefaultPosition, wx.Size(20, -1),
style=wx.TE_READONLY)
boxsizerX1X2.Add (tctrlVar2, expand, wx.CENTER|wx.ALL, 10) # a
10-pixel border all around
boxsizerX1X2.Add (boxsizerX1V, expand, wx.CENTER|wx.ALL, 10) # a
10-pixel border all around
boxsizerX1X2.Add (boxsizerX2V, expand, wx.CENTER|wx.ALL, 10) # a
10-pixel border all around
return boxsizerX1X2
#end def VarControlsVar2
def VarControlsVar3 (self, parentPanel, varLabelChar):
global tctrlVar3
def OnSpinX1 (event):
global x1Var3
x1Var3 = (event.GetPosition() - 200) / 200.0 # raw spinbutton
value
self.textX1Var3.SetValue ('%1.3f' % (x1Var3))
#end def
def OnSpinX2 (event):
global x2Var3
x2Var3 = (event.GetPosition() - 200) / 200.0 # raw spinbutton
value
self.textX2Var3.SetValue ('%1.3f' % (x2Var3))
#end def
def ResetX1BtnHandler (event):
global x1Var3
self.spinX1.SetValue (200) # 0.000
x1Var3 = 0.00
self.textX1Var3.SetValue ('%1.3f' % (x1Var3))
#end def ResetX1BtnHandler
def ResetX2BtnHandler (event):
global x2Var3
self.spinX2.SetValue (400) # 0.000
x2Var3 = 1.00
self.textX2Var3.SetValue ('%1.3f' % (x2Var3))
#end def ResetX1BtnHandler
#--------------------
expand = False
hgt = 40
padding = 0
boxsizerX1X2 = wx.BoxSizer (wx.HORIZONTAL)
boxsizerX1V = wx.BoxSizer (wx.VERTICAL)
labelX1 = wx.StaticText (self, -1, "X1: Y=0.0 Intercept:",
wx.DefaultPosition)
boxsizerX1V.Add (labelX1, expand, wx.CENTER|wx.ALL, padding)
boxsizerX1 = wx.BoxSizer (wx.HORIZONTAL)
self.textX1Var3 = wx.TextCtrl (self, -1, "0.000", wx.DefaultPosition,
wx.Size(60, -1),
style=wx.TE_READONLY)
boxsizerX1.Add (self.textX1Var3, expand, wx.CENTER|wx.ALL, padding)
self.spinX1 = wx.SpinButton (self, 20, wx.DefaultPosition, wx.Size(hgt,
hgt), wx.SP_HORIZONTAL)
self.spinX1.SetRange (0, 600) # -1.000 .. 2.000
self.spinX1.SetValue (200) # x1 = 0.000
wx.EVT_SPIN (self.spinX1, 20, OnSpinX1)
boxsizerX1.Add (self.spinX1, expand, 0, padding)
boxsizerX1V.Add (boxsizerX1, expand, 0, padding)
resetX1btn = wx.Button (self, -1, "Reset to 0.000")
boxsizerX1V.Add (resetX1btn, expand, 0, padding)
wx.EVT_BUTTON (self, resetX1btn.GetId(), ResetX1BtnHandler)
boxsizerX2V = wx.BoxSizer (wx.VERTICAL)
labelX2 = wx.StaticText (self, -1, "X2: Y=1.0 Intercept:",
wx.DefaultPosition)
boxsizerX2V.Add (labelX2, expand, wx.CENTER|wx.ALL, padding)
boxsizerX2 = wx.BoxSizer (wx.HORIZONTAL)
self.textX2Var3 = wx.TextCtrl (self, -1, "1.000", wx.DefaultPosition,
wx.Size(60, -1),
style=wx.TE_READONLY)
boxsizerX2.Add (self.textX2Var3, expand, wx.CENTER|wx.ALL, padding)
self.spinX2 = wx.SpinButton (self, 20, wx.DefaultPosition, wx.Size(hgt,
hgt), wx.SP_HORIZONTAL)
self.spinX2.SetRange (0, 600) # -1.000 .. 2.000
self.spinX2.SetValue (400) # x2 = 1.000
wx.EVT_SPIN (self.spinX2, 20, OnSpinX2)
boxsizerX2.Add (self.spinX2, expand, wx.CENTER|wx.ALL, padding)
boxsizerX2V.Add (boxsizerX2, expand, 0, padding)
resetX2btn = wx.Button (self, -1, "Reset to 1.000")
boxsizerX2V.Add (resetX2btn, expand, 0, padding)
wx.EVT_BUTTON (self, resetX2btn.GetId(), ResetX2BtnHandler)
tctrlVar3 = wx.TextCtrl (self, -1, varLabelChar.upper(),
wx.DefaultPosition, wx.Size(20, -1),
style=wx.TE_READONLY)
boxsizerX1X2.Add (tctrlVar3, expand, wx.CENTER|wx.ALL, 10) # a
10-pixel border all around
boxsizerX1X2.Add (boxsizerX1V, expand, wx.CENTER|wx.ALL, 10)
boxsizerX1X2.Add (boxsizerX2V, expand, wx.CENTER|wx.ALL, 10)
return boxsizerX1X2
#end def VarControlsVar3
def VarControlsVar4 (self, parentPanel, varLabelChar):
global tctrlVar4
def OnSpinX1 (event):
global x1Var4
x1Var4 = (event.GetPosition() - 200) / 200.0 # raw spinbutton
value
self.textX1Var4.SetValue ('%1.3f' % (x1Var4))
#end def
def OnSpinX2 (event):
global x2Var4
x2Var4 = (event.GetPosition() - 200) / 200.0 # raw spinbutton
value
self.textX2Var4.SetValue ('%1.3f' % (x2Var4))
#end def
def ResetX1BtnHandler (event):
global x1Var4
self.spinX1.SetValue (200) # 0.000
x1Var4 = 0.00
self.textX1Var4.SetValue ('%1.3f' % (x1Var4))
#end def ResetX1BtnHandler
def ResetX2BtnHandler (event):
global x2Var4
self.spinX2.SetValue (400) # 0.000
x2Var4 = 1.00
self.textX2Var4.SetValue ('%1.3f' % (x2Var4))
#end def ResetX1BtnHandler
#--------------------
expand = False
hgt = 40
padding = 0
boxsizerX1X2 = wx.BoxSizer (wx.HORIZONTAL)
boxsizerX1V = wx.BoxSizer (wx.VERTICAL)
labelX1 = wx.StaticText (self, -1, "X1: Y=0.0 Intercept:",
wx.DefaultPosition)
boxsizerX1V.Add (labelX1, expand, wx.CENTER|wx.ALL, padding)
boxsizerX1 = wx.BoxSizer (wx.HORIZONTAL)
self.textX1Var4 = wx.TextCtrl (self, -1, "0.000", wx.DefaultPosition,
wx.Size(60, -1),
style=wx.TE_READONLY)
boxsizerX1.Add (self.textX1Var4, expand, wx.CENTER|wx.ALL, padding)
self.spinX1 = wx.SpinButton (self, 20, wx.DefaultPosition, wx.Size(hgt,
hgt), wx.SP_HORIZONTAL)
self.spinX1.SetRange (0, 600) # -1.000 .. 2.000
self.spinX1.SetValue (200) # x1 = 0.000
wx.EVT_SPIN (self.spinX1, 20, OnSpinX1)
boxsizerX1.Add (self.spinX1, expand, 0, padding)
boxsizerX1V.Add (boxsizerX1, expand, 0, padding)
resetX1btn = wx.Button (self, -1, "Reset to 0.000")
boxsizerX1V.Add (resetX1btn, expand, 0, padding)
wx.EVT_BUTTON (self, resetX1btn.GetId(), ResetX1BtnHandler)
boxsizerX2V = wx.BoxSizer (wx.VERTICAL)
labelX2 = wx.StaticText (self, -1, "X2: Y=1.0 Intercept:",
wx.DefaultPosition)
boxsizerX2V.Add (labelX2, expand, wx.CENTER|wx.ALL, padding)
boxsizerX2 = wx.BoxSizer (wx.HORIZONTAL)
self.textX2Var4 = wx.TextCtrl (self, -1, "1.000", wx.DefaultPosition,
wx.Size(60, -1),
style=wx.TE_READONLY)
boxsizerX2.Add (self.textX2Var4, expand, wx.CENTER|wx.ALL, padding)
self.spinX2 = wx.SpinButton (self, 20, wx.DefaultPosition, wx.Size(hgt,
hgt), wx.SP_HORIZONTAL)
self.spinX2.SetRange (0, 600) # -1.000 .. 2.000
self.spinX2.SetValue (400) # x2 = 1.000
wx.EVT_SPIN (self.spinX2, 20, OnSpinX2)
boxsizerX2.Add (self.spinX2, expand, wx.CENTER|wx.ALL, padding)
boxsizerX2V.Add (boxsizerX2, expand, 0, padding)
resetX2btn = wx.Button (self, -1, "Reset to 1.000")
boxsizerX2V.Add (resetX2btn, expand, 0, padding)
wx.EVT_BUTTON (self, resetX2btn.GetId(), ResetX2BtnHandler)
tctrlVar4 = wx.TextCtrl (self, -1, varLabelChar.upper(),
wx.Point(0, 0), wx.Size(20, -1),
style=wx.TE_READONLY)
boxsizerX1X2.Add (tctrlVar4, expand, wx.CENTER|wx.ALL, 10) # a
10-pixel border all around
boxsizerX1X2.Add (boxsizerX1V, expand, wx.CENTER|wx.ALL, 10) # a
10-pixel border all around
boxsizerX1X2.Add (boxsizerX2V, expand, wx.CENTER|wx.ALL, 10) # a
10-pixel border all around
return boxsizerX1X2
#end def VarControlsVar4
def xformBtnHandler (self, event):
global filepathname
if (type (filepathname)==type('abc')) or
(type(filepathname)==type(u'abc')):
try:
self.childFrame.Destroy()
except:
pass
#end if
# Transform the original image into a new one.
self.ChildFrame()
#end if
#end def xformBtnHandler
def ChildFrame (self):
global filepathname
global thumbnailXsize
global thumbnailYsize
global x1Var1
global x2Var1
childframeXpsn = self.mainframePsn[0] + 2*self.mainframeSize[0]
childframepsn = (childframeXpsn, self.mainframePsn[1])
childframesize = (300, 300)
childFrame = wx.Frame (self.parentFrame, -1, "Image
Reverse-Transformed", childframepsn, childframesize)
# Create the child panel
self.childPanel = wx.Panel (childFrame, -1)
self.childPanel.SetBackgroundColour ('#C9E3E5')
# Create the child's bitmap
staticBitmap = wx.StaticBitmap (self.childPanel, -1, wx.EmptyBitmap(5,
5))
# Get and resize the original image to fit in the child panel and store
it to a bitmap
imPil = Image.open (filepathname)
imPil = imPil.resize ( (thumbnailXsize, thumbnailYsize) )
# Transform the thumbnail image. This is where all the hard stuff is
performed.
imRgbPrime = Brighten (imPil) #
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
bitmap = Pil2Bitmap (imRgbPrime)
staticBitmap.SetBitmap (bitmap)
# Add the static bitmap to the panel.
boxsizer = wx.BoxSizer (wx.VERTICAL)
expand = False
boxsizer.Add (staticBitmap, expand)
self.childPanel.SetAutoLayout (True)
self.childPanel.SetSizer (boxsizer)
# Show the child frame
childFrame.Show (True)
self.childFrame = childFrame
#end def ChildFrame
#end class AppPanel
def GetBitmapFromFile (filepathname, framesize):
global thumbnailXsize
global thumbnailYsize
# If too large, resize the image to fit in the panel space.
imPil = Image.open (filepathname)
imXsize, imYsize = imPil.size
panelXextent = framesize[0] - 10 # border size estimations for MS Windows
panelYextent = framesize[1] - 24
if (imXsize > panelXextent) or (imYsize > panelYextent):
# The "primary" axis is the one that must be scaled down to fit its
extent.
# The "secondary" axis will automatically scale to fit within its
extent.
# Find which axis is the primary.
x2yExtentRatio = 1.0 * panelXextent / panelYextent
imageXYratio = 1.0 * imXsize / imYsize
if imageXYratio > x2yExtentRatio: # The X axis is the primary axis
# Find the image scale factor using the X axis.
scalefactor = (1.0 * panelXextent) / imXsize
else:
scalefactor = (1.0 * panelYextent) / imYsize
#end if
# Resize the original image
newXsize = int (scalefactor * imXsize)
newYsize = int (scalefactor * imYsize)
imPil = imPil.resize ( (newXsize, newYsize) )
#end if
# Convert the Pil image to a bitmap and install it into the displayed
static bitmap
bitmap = Pil2Bitmap (imPil)
thumbnailXsize = newXsize # save globally; child frame will use these
thumbnailYsize = newYsize
return bitmap
#end def GetBitmapFromFile
def Brighten (imRgb):
global colorspaceStr
global x1Var1, x2Var1
global x1Var2, x2Var2
global x1Var3, x2Var3
global x1Var4, x2Var4
# Extract the data from the input image and convert it to the particular
colorspace data.
rgbData = list (imRgb.getdata()) # must make a list of tuples out of the
special sequence
#print 'Transforming RGB data to a new colorspace ...',
spaceData = []
for rgbtuple in rgbData:
if colorspaceStr.lower() == 'hsl-':
spaceData.append (Rgb2Hsl (rgbtuple))
elif colorspaceStr.lower() == 'hsv-':
spaceData.append (Rgb2Hsv (rgbtuple))
elif colorspaceStr.lower() == 'cmyk':
spaceData.append (Rgb2Cmyk (rgbtuple))
else:
print
print 'Brighten(1): Colorspace string [%s] not known.' %
(colorspaceStr.upper())
print
os._exit (1)
#end if
#end for
#print 'Finished'
# Calculate the slope and offset (derived from y = mx + b).
slopeVar1 = 1.0e6 # arbitrary maximum value of slope: avoid
division-by-zero
if x1Var1 != x2Var1: slopeVar1 = 1.0/(x2Var1 - x1Var1)
offsetVar1 = -1.0 * slopeVar1 * x1Var1
slopeVar2 = 1.0e6 # arbitrary maximum
if x1Var2 != x2Var2: slopeVar2 = 1.0/(x2Var2 - x1Var2)
offsetVar2 = -1.0 * slopeVar2 * x1Var2
slopeVar3 = 1.0e6 # arbitrary maximum
if x1Var3 != x2Var3: slopeVar3 = 1.0/(x2Var3 - x1Var3)
offsetVar3 = -1.0 * slopeVar3 * x1Var3
slopeVar4 = 1.0e6 # arbitrary maximum
if x1Var4 != x2Var4: slopeVar4 = 1.0/(x2Var4 - x1Var4)
offsetVar4 = -1.0 * slopeVar4 * x1Var4
# Modify the particular colorspace values
if colorspaceStr.lower() == 'hsl-':
for index in xrange (len(spaceData)):
h, s, l = spaceData [index]
# Apply an arbitrary algorithm to modify the colorspace vectors.
h = (h * slopeVar1) + offsetVar1
s = (s * slopeVar2) + offsetVar2
l = (l * slopeVar3) + offsetVar3
spaceData [index] = (h, s, l)
#end for
elif colorspaceStr.lower() == 'hsv-':
for index in xrange (len(spaceData)):
h, s, v = spaceData [index]
# Apply an arbitrary algorithm to modify the colorspace vectors.
h = (h * slopeVar1) + offsetVar1
s = (s * slopeVar2) + offsetVar2
v = (v * slopeVar3) + offsetVar3
spaceData [index] = (h, s, v)
#end for
elif colorspaceStr.lower() == 'cmyk':
for index in xrange (len(spaceData)):
c, m, y, k = spaceData [index]
# Apply an arbitrary algorithm to modify the colorspace vectors.
c = (c * slopeVar1) + offsetVar1
m = (m * slopeVar2) + offsetVar2
y = (y * slopeVar3) + offsetVar3
k = (k * slopeVar4) + offsetVar4
spaceData [index] = (c, m, y, k) # replace old values with new
in-place
#end for
else:
print
print 'Brighten(2): Colorspace string [%s] not known.' %
(colorspaceStr.upper())
print
os._exit (1)
#end if
# Convert the modified colorspace data back into RGB data
#print 'Returning modified colorspace data to RGB ...',
rgbPrimedata = []
for spacetuple in spaceData:
if colorspaceStr.lower() == 'cmyk':
rgbPrimetuple = Cmyk2Rgb (spacetuple)
elif colorspaceStr.lower() == 'hsl-':
rgbPrimetuple = Hsl2Rgb (spacetuple)
elif colorspaceStr.lower() == 'hsv-':
rgbPrimetuple = Hsv2Rgb (spacetuple)
else:
print
print 'Brighten(3): Colorspace string not recognized = [%s]' %
(colorspaceStr.upper())
print
os._exit (1)
#end if
rgbPrimedata.append (rgbPrimetuple)
#end for
#print 'Finished'
# Create a new image from the new RGB data
imRgbPrime = Image.new ('RGB', imRgb.size)
imRgbPrime.putdata (rgbPrimedata)
return imRgbPrime
#end def Brighten
#------------------
if __name__ == "__main__":
app = wx.PySimpleApp()
wx.InitAllImageHandlers()
framepsn = (100, 100)
framesize = (300, 575) # master frame size
AppFrame (None, -1, "Colorspace Transformer", framepsn, framesize)
app.MainLoop()
#end if
_______________________________________________ Image-SIG maillist - [email protected] http://mail.python.org/mailman/listinfo/image-sig
