[Image-SIG] HSL adjustment, and others
Ray Pasco
pascor at hotpop.com
Thu Apr 7 16:31:58 CEST 2005
I've been playing around with just this. This program converts to/from
several different colorspaces
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
-------------- next part --------------
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
#----------------------------
-------------- next part --------------
## 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
-------------- next part --------------
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
More information about the Image-SIG
mailing list