CSUMB 205 Multimedia in PYTHON  0.8.12
An easy-to-use wrapper module for the PIL module
simpleImage.py
1 #!/usr/local/bin/python3
2 """
3 -------------------------------------------------------------------------------
4 SIMPLE IMAGE.PY
5 
6 AUTHOR(S): Peter Walker pwalker@csumb.edu
7 
8 PURPOSE- This module will be used by CST 205 for image manipulation. It
9  creates some classes that will use the PIL package (installed
10  by calling 'pip3 install Pillow')
11 
12 CLASSES-
13  rgbPixel
14  rgbImage
15 -------------------------------------------------------------------------------
16 """
17 
18 
19 # IMPORTS
20 from platform import system
21 import os
22 #import sys
23 #import subprocess
24 from numpy import asarray, uint8
25 from tkinter.filedialog import (askdirectory, askopenfilename)
26 from tkinter import Tk
27 from PIL import Image
28 #END IMPORTS
29 
30 
31 
32 
33 class rgbPixel(object):
34 
35  """
36  A simple class for accessing attributes of an RGB Pixel
37 
38  ATTRIBUTES:
39  red An integer between 0 and 255.
40  Accessible by getRed() and setRed()
41  blue An integer between 0 and 255.
42  Accessible by getBlue() and setBlue()
43  green An integer between 0 and 255.
44  Accessible by getGreen() and setGreen()
45  X An integer, representing the pixel's location in a picture.
46  Accesible with getX()
47  Y An integer, representing the pixel's location in a picture.
48  Accesible with getY()
49  """
50 
51  # CLASS ATTRIBUTES -----------
52  __red = 0
53  __green = 0
54  __blue = 0
55  __x=0
56  __y=0
57  # ----------------------------
58 
59 
60  def __init__(self, *args):
61  """
62  Object initialization
63  ARGS:
64  self this Object
65  (r,g,b) Tuple/List of pixel's color's R,G, and B values
66  or
67  r A pixel value for the Red aspect
68  g A pixel value for the Green aspect
69  b A pixel value for the Blue aspect
70 
71  (x,y) Tuple/List of the pixel's X and Y coordinate
72  or
73  x The X-part of the this pixel's location within the picture
74  y The Y-part of the this pixel's location within the picture
75  """
76  #The return of setColor() is True if args[0] is a list or tuple
77  if self.setColor(*args):
78  args = args[1:]
79  else:
80  args = args[3:]
81  #END IF/ELSE
82  self.__setCoord(*args)
83  #END DEF
84 
85 
86  # CLASS SPECIFIC DECORATORS and VALUE CHECKERS -----------------------------
87 
88  def __checkVal(func):
89  """
90  A decorator that will make sure that the color arguments passed are viable.
91  The arguments we are expected are "self", and an integer value between
92  0 and 255.
93  """
94  def wrapper(*args, **kwargs):
95  if not isinstance(args[1], (int,float)):
96  raise ValueError("You must pass in a number value. Was passed {}".format(repr(args[1])))
97  #Casting args to a list so that we can modify the values (args was originally
98  # a tuple, which is immutable)
99  args = list(args)
100  args[1] = int(args[1])
101  #This will make sure that the value is between 0 and 255, by iteratively
102  # adding or subtracting 256
103  while args[1] > 255 or args[1] < 0:
104  if args[1] < 0:
105  args[1] += 256
106  elif args[1] > 255:
107  args[1] -= 256
108  #END WHILE
109  #Casting args back to being a tuple
110  args = tuple(args)
111  return func(*args, **kwargs)
112  return wrapper
113  #END DEF
114 
115  def __checkCoord(func):
116  """
117  A decorator that will make sure that the coordinate arguments passed are viable.
118  The arguments we are expected are "self", and an integer value greater than 0.
119  """
120  def wrapper(*args, **kwargs):
121  if not isinstance(args[1], (int,float)):
122  raise ValueError("You must pass in a number value. Was passed {}".format(repr(args[1])))
123  #Checking for negative coordinate
124  if args[1]<0:
125  raise ValueError("You must pass in a POSITIVE number value. "+
126  "Was passed {}".format(repr(args[1])))
127  #Casting args to a list so that we can modify the values (args was originally
128  # a tuple, which is immutable)
129  args = list(args)
130  args[1] = float(args[1])
131  #Casting args back to being a tuple
132  args = tuple(args)
133  return func(*args, **kwargs)
134  return wrapper
135  #END DEF
136 
137  def __sameObject(func):
138  def wrapper(*args, **kwargs):
139  if not isinstance(args[1], args[0].__class__):
140  raise TypeError("You must compare objects of the same type. Cannot compare "+
141  "{0} and {1}".format(args[0].__class__, args[1].__class__))
142  return func(*args, **kwargs)
143  return wrapper
144  #END DEF
145 
146  def __isPixel(self, pixel):
147  """Simple function to make sure that the passed object is a pixel"""
148  if not isinstance(pixel, self.__class__):
149  raise ValueError("You must give an rgbPixel object to 'pixel'")
150  return True
151  #END DEF
152 
153  def __isColor(self, color):
154  """Simple function to make sure that the passed color has 3 integers"""
155  if not isinstance(color, (tuple,list)) and len(color) != 3:
156  raise ValueError("You must give an RGB value as a tuple (r,g,b) to 'color'")
157  if not all( [isinstance(val, int) for val in list(color)] ):
158  raise ValueError("You must give three RGB values as integers to the keyword arg 'color'")
159  return True
160  #END DEF
161 
162 
163  # GETTERS AND SETTERS ------------------------------------------------------
164 
165  #Color setting functions
166  @__checkVal
167  def setRed(self, val):
168  """Sets the RED values to given number"""
169  self.__red = val
170  def getRed(self):
171  """Gets the RED value of the picture"""
172  return self.__red
173 
174  @__checkVal
175  def setGreen(self, val):
176  """Sets the GREEN values to given number"""
177  self.__green = val
178  def getGreen(self):
179  """Gets the GREEN value of the picture"""
180  return self.__green
181 
182  @__checkVal
183  def setBlue(self, val):
184  """Sets the BLUE values to given number"""
185  self.__blue = val
186  def getBlue(self):
187  """Gets the BLUE value of the picture"""
188  return self.__blue
189 
190  #Coordinate setting functions
191  @__checkCoord
192  def __setX(self, coord):
193  self.__x = coord
194  def getX(self):
195  """Gets the X-coordinate of this pixel in the picture"""
196  return self.__x
197 
198  @__checkCoord
199  def __setY(self, coord):
200  self.__y = coord
201  def getY(self):
202  """Gets the Y-coordinate of this pixel in the picture"""
203  return self.__y
204 
205  #Setters and Getters for multiple attributes
206  def setColor(self, *args):
207  """
208  Sets this pixel's R,G, and B values to the given values
209  ARGS:
210  (R,G,B) Tuple/List of three integers
211  or
212  R Integer value, the red value of the color
213  G Integer value, the green value of the color
214  B Integer value, the blue value of the color
215  """
216  if len(args) == 0:
217  raise ValueError("You must pass in 2 NUMBER values (as a tuple or separately). "+
218  "You passed in {}".format(len(args)))
219  if not isinstance(args[0], (list,tuple)):
220  color = list(args[:3])
221  _usingList = False
222  else:
223  color = list(args[0][:3])
224  _usingList = True
225  if len(color)!=3:
226  raise ValueError("You must pass in 3 NUMBER values (as a tuple or separately). "+
227  "You passed in {}".format(len(color)))
228  self.setRed(color[0])
229  self.setGreen(color[1])
230  self.setBlue(color[2])
231  return _usingList
232  #END DEF
233 
234  def getColor(self):
235  """Gets all 3 RGB values of this picture as a tuple"""
236  return self._color_as_tuple()
237 
238  def __setCoord(self, *args):
239  """
240  Sets this pixel's X and Y coordinate to the given values
241  ARGS:
242  (x,y) Tuple/List of the pixel's X and Y coordinate
243  or
244  x The X-part of the this pixel's location within the picture
245  y The Y-part of the this pixel's location within the picture
246  """
247  if len(args) == 0:
248  raise ValueError("You must pass in 2 NUMBER values (as a tuple or separately). "+
249  "You passed in {}".format(len(args)))
250  if not isinstance(args[0], (list,tuple)):
251  coord = list(args[:2])
252  _usingList = False
253  else:
254  coord = list(args[0][:2])
255  _usingList = True
256  if len(coord)!=2:
257  raise ValueError("You must pass in 2 NUMBER values (as a tuple or separately). "+
258  "You passed in {}".format(len(coord)))
259  self.__setX(coord[0])
260  self.__setY(coord[1])
261  return _usingList
262  #END DEF
263 
264  def getCoord(self):
265  """Gets the X and Y coordinate of this pixel as a tuple"""
266  return self._coord_as_tuple()
267 
268 
269  # CONVERTERS ---------------------------------------------------------------
270 
271  def _color_as_array(self):
272  """Returns the pixel's RGB values in a list format"""
273  return [self.__red, self.__green, self.__blue]
274 
275  def _color_as_tuple(self):
276  """Returns the pixel's RGB values in a tuple format"""
277  return (self.__red, self.__green, self.__blue)
278 
279  def _coord_as_array(self):
280  """Returns the pixel's RGB values in a list format"""
281  return [self.__x, self.__y]
282 
283  def _coord_as_tuple(self):
284  """Returns the pixel's RGB values in a tuple format"""
285  return (self.__x, self.__y)
286 
287 
288  # FUN PIXEL MATH FUNCTIONS -------------------------------------------------
289 
290  def cdist(self, **kwargs):
291  """
292  Gets the color distance between this pixel and a specified color or pixel object
293  ARGS:
294  color tuple, 3 integer values where 0<=x<=255
295  pixel an rgbPixel object
296  RETURNS:
297  A Float, that is this pixel's color's distance to the given rgbPixel or
298  color tuple.
299  A negative number means that this pixel will likely be lighter, while
300  a positive number means that this pixel will likely be darker
301  """
302  if not any( [elem in kwargs for elem in ["color", "pixel"]] ):
303  raise KeyError("You must pass either a color or pixel through the keywords 'color' or 'pixel'")
304  if "color" in kwargs:
305  self.__isColor(kwargs['color'])
306  elif "pixel" in kwargs:
307  self.__isPixel(kwargs['pixel'])
308  #END IF/ELIF
309  if "color" in kwargs:
310  givenR, givenG, givenB = kwargs['color']
311  elif "pixel" in kwargs:
312  givenR, givenG, givenB = kwargs['pixel']._color_as_tuple()
313  rDist = givenR-self.__red
314  gDist = givenG-self.__green
315  bDist = givenB-self.__blue
316  return ((rDist+gDist+bDist)/3.0)
317  #END DEF
318 
319  def ldist(self, pixel):
320  """
321  Returns the length distance between two rgbPixels according to their
322  X and Y values.
323  """
324  self.__isPixel(pixel)
325  xDist = pixel.getX()-self.getX()
326  yDist = pixel.getY()-self.getY()
327  from math import sqrt
328  return sqrt(xDist**2 + yDist**2)
329  #END DEF
330 
331  def _avgValue(self):
332  return int((self.__red + self.__green + self.__blue)/3)
333  #END DEF
334 
335 
336  # STRING FUNCTIONS ---------------------------------------------------------
337 
338  def __str__(self):
339  """Returns a string representation of this object"""
340  return ("Pixel at ({},{}): ".format(self.__x, self.__y)+
341  "RED={}, GREEN={}, BLUE={}".format(self.__red, self.__green, self.__blue))
342  #END DEF
343 
344 
345  # COMPARATORS --------------------------------------------------------------
346 
347  @__sameObject
348  def __lt__(self, other):
349  """Less than comparison, based on average pixel value"""
350  if self._avgValue() < other._avgValue():
351  return True
352  return False
353  #END DEF
354  @__sameObject
355  def __eq__(self, other):
356  """Equal comparison, based on average pixel value"""
357  if self._avgValue() == other._avgValue():
358  return True
359  return False
360  #END DEF
361 
362  def __gt__(self, other):
363  return not self.__eq__(other) and not self.__lt__(other)
364  def __ne__(self, other):
365  return not self.__eq__(other)
366  def __ge__(self, other):
367  return self.__eq__(other) or self.__gt__(other)
368  def __le__(self, other):
369  return self.__eq__(other) or self.__le__(other)
370 #END CLASS ---------------------------------------------------------------------
371 
372 
373 
374 
375 
376 
377 class rgbImage(object):
378 
379  """
380  A simple class for accessing attributes of an RGB Pixel
381 
382  ATTRIBUTES:
383  inputFilename String, the absolute path of the file used for input.
384  Empty if blank image was created
385  outputFilename String, the absolute path where the image will be saved.
386  Empty if blank image was created
387  pixels List, a 2-dimensional array of rgbPixel objects
388  height Integer, the height of the picture in pixels
389  width Integer, the width of the pixture in pixels
390  """
391 
392  # CLASS ATTRIBUTES -----------
393  inputFilename = ""
394  outputFilename = ""
395 
396  __myImage = None
397  __blank = False
398  pixels = []
399 
400  height = 0
401  width = 0
402  # ----------------------------
403 
404 
405  def __init__(self, inputFilename="", outputFilename="", blank=False, width=100, height=100):
406  """
407  Object initialization
408  ARGS:
409  self this Object
410  inputFilename String, the location of the picture
411  outputFilename String, the location that this picture will be saved to
412  blank Boolean, whether this image should be a blank image
413  width Integer, the width of the blank image
414  height Integer, the height of the blank image
415  """
416  self.__blank = blank
417  if not self.__blank:
418  if not inputFilename:
419  #These three lines are here because....
420  __ = Tk()
421  __.update()
422  __.withdraw() #Don't know why, but this makes it so that the file dialog leaves
423  #Now we ask for our file name
424  self.inputFilename = askopenfilename(initialdir=os.path.expanduser("~"),
425  title="Select the IMAGE FILE",
426  filetypes=[('JPG Image', '.jpg'),
427  ('JPEG Image', '.jpeg'),
428  ('GIF Image', '.gif'),
429  ('PNG Image', '.png')
430  ],
431  multiple=False
432  )
433  if not self.inputFilename:
434  raise ValueError("You must choose a file for this class to work.")
435  self.inputFilename = os.path.abspath(self.inputFilename)
436  else:
437  self.inputFilename = os.path.abspath(inputFilename)
438  if system()!="Windows":
439  self.inputFilename = self.inputFilename.replace("\\","").strip()
440  acceptedExt = ["jpg","jpeg","png","gif"]
441  if not os.path.isfile(self.inputFilename) or \
442  self.inputFilename.split(".")[-1].lower() not in acceptedExt:
443  raise ValueError("You must pass in a legitimate picture file. "+
444  "Use one of the following options:"+str(acceptedExt))
445  #END IF/ELSE
446  if not outputFilename:
447  dirname = os.path.dirname(self.inputFilename)
448  filename = os.path.basename(self.inputFilename)
449  filename = filename.split(".")[0]+"_altered."+filename.split(".")[1]
450  self.outputFilename = os.path.join(dirname, filename)
451  else:
452  self.outputFilename = os.path.abspath(outputFilename)
453  #END IF/ELSE
454  if os.path.isfile(self.outputFilename):
455  print("{} will be overwritten if this image is saved. ".format(os.path.basename(self.outputFilename))+
456  "Consider saving to another location."
457  )
458  #END IF
459  print("Importing image {}".format(self.inputFilename))
460  else:
461  self.width, self.height = width, height
462  print("Creating Blank Image, {}x{}".format(self.width, self.height))
463  #END IF/ELSE
464 
465  #Calls self.reset(), as the reset function does what we want to do initially
466  self.reset()
467  #END DEF
468 
469 
470  # CLASS SPECIFIC DECORATORS and CHECKERS -----------------------------------
471 
472  def __checkCoordPair(func):
473  def coord_wrapper(*args, **kwargs):
474  def checkPoint(val, maxVal):
475  if val<0 or val>maxVal:
476  return False
477  return True
478  #END DEF
479 
480  if isinstance(args[1], (tuple,list)):
481  if len(args[1])!=2:
482  raise ValueError("You must pass in two values as a coordinate. "+
483  "Was passed {}".format(repr(args[1]))
484  )
485  if not all([isinstance(coord, int) for coord in args[1]]):
486  raise ValueError("You must pass in two integers for coordinates. "+
487  "Was passed {}".format(repr(args[1]))
488  )
489  x,y = tuple(args[1])
490  if not checkPoint(x, args[0].width-1) and not checkPoint(y, args[0].height-1):
491  raise ValueError("You must pass in POSITIVE integers. "+
492  "Was passed {}".format(repr([x,y]))
493  )
494  else:
495  if not all( [isinstance(elem,int) for elem in [args[1],args[2]]] ):
496  raise ValueError("You must pass in integer value. "+
497  "Was passed {}".format(repr([args[1],args[2]]))
498  )
499  x = args[1]
500  y = args[2]
501  if not checkPoint(x, args[0].width-1) and not checkPoint(y, args[0].height-1):
502  raise ValueError("You must pass in POSITIVE integers. "+
503  "Was passed {}".format(repr([x,y]))
504  )
505  #END IF/ELSE
506  return func(*args, **kwargs)
507  return coord_wrapper
508  #END DEF
509 
510 
511  # GETTERS ------------------------------------------------------------------
512 
513  @__checkCoordPair
514  def getPixel(self, *args):
515  """
516  Gets an rgbPixel object at the specified (x,y) coordinate
517  ARGS:
518  self this Object
519  x The X coordinate of the pixel
520  y The Y coordinate of the pixel
521  or
522  (x,y) The X and Y coordinate within a tuple or list
523  RETURNS:
524  rgbPixel object
525  RAISES:
526  ValueError - 'x' or 'y' is out of the picture's bounds
527  """
528  #Getting the X and Y from the passed arguments
529  if isinstance(args[0], (tuple, list)):
530  x,y = args[0]
531  else:
532  x = args[0]
533  y = args[1]
534  return self.pixels[y][x]
535  #END DEF
536 
537  def getAllPixels(self):
538  """Converts this object's 'pixels' attribute into a 1-dimensional array"""
539  allPixels = []
540  for row in self.pixels:
541  for pixel in row:
542  allPixels.append(pixel)
543  return allPixels
544  #END DEF
545 
546  def setName(self, name):
547  """
548  Given a new name (as a String), sets the output filename
549  ARGS:
550  name String, the new name of the to-be-saved file
551  RETURNS:
552  Boolean, True if file does not exist, False otherwise
553  RAISES:
554  Value Error if name is not string
555  """
556  if not isinstance(name, str):
557  raise ValueError("You must pass in a string for your new image name")
558  #If the path given is an absolute path, just set that
559  if os.path.isabs(name):
560  self.outputFilename = os.path.abspath(name)
561  #If the image was a blank image, then we open a file dialog
562  elif self.__blank and not self.outputFilename:
563  print("Please choose the directory you will want the file saved in...")
564  #These three lines are here because....
565  __ = Tk()
566  __.update()
567  __.withdraw() #Don't know why, but this makes it so that the file dialog leaves
568  #Now we ask for our file name
569  outputPath = askdirectory(initialdir=os.path.expanduser("~"),
570  title="Select the FOLDER to contain the image",
571  mustexist=True)
572  name = os.path.splitext(name)[0]+".png"
573  self.outputFilename = os.path.abspath(os.path.join(outputPath, name))
574  #If the image was not blank, then we see if outputFilename is set. If not, we
575  # will use the inputFilename as a base folder
576  elif not self.outputFilename:
577  dirname = os.path.dirname(self.inputFilename)
578  name = os.path.basename(name)+"."+os.path.splitext(self.inputFilename)[1]
579  self.outputFilename = os.path.abspath(os.path.join(dirname, name))
580  #Otherwise, we use the already existing outputFilename as a base
581  else:
582  dirname = os.path.dirname(self.outputFilename)
583  name = os.path.basename(name)+"."+os.path.splitext(self.outputFilename)[1]
584  self.outputFilename = os.path.abspath(os.path.join(dirname, name))
585  #END IF/ELSE
586  if os.path.isfile(self.outputFilename):
587  print("{} will be overwritten if this image is saved. ".format(os.path.basename(self.outputFilename))+
588  "Consider saving to another location."
589  )
590  return False
591  #END IF
592  return True
593  #END DEF
594 
595  def save(self, filename="", forceOverwrite=False):
596  """
597  This will save the current object to a specified location
598  ARGS:
599  self this Object
600  filename OPTIONAL. String, the name or location to be saved to.
601  forceOverwrite OPTIONAL. Boolean, whether to force overwrite of file
602  at location 'filename' or 'self.outputFilename'
603  RETURNS:
604  none
605  RAISES:
606  none.
607  """
608  if self.outputFilename=="":
609  print("Please choose the directory you will want the file saved in...")
610  #These three lines are here because....
611  __ = Tk()
612  __.update()
613  __.withdraw() #Don't know why, but this makes it so that the file dialog leaves
614  #Now we ask for our file name
615  outputPath = askdirectory(initialdir=os.path.expanduser("~"),
616  title="Select the FOLDER to contain the image",
617  mustexist=True)
618  name = "simpleImage_output.jpg"
619  self.outputFilename = os.path.abspath(os.path.join(outputPath, name))
620  if filename!="":
621  self.setName(filename)
622  #array needs to be an array of rows, each row needs to be an array of
623  # pixels, and each pixel an array of pixel values (R,G,B)
624  img_to_array = asarray(self.__as_array())
625  Image.fromarray(img_to_array).save(self.outputFilename)
626  print("File saved at {}".format(self.outputFilename))
627  #END DEF
628 
629  def show(self):
630  """Shows the image the user is currently working on"""
631  img_to_array = asarray(self.__as_array())
632  Image.fromarray(img_to_array).show()
633  #END DEF
634 
635  def reset(self):
636  """Resets this object to the original image"""
637  #We need to open the file to get the data from it. If self.__blank is not
638  # true, then we need to create a new image.
639  if not self.__blank:
640  self.__myImage = Image.open(self.inputFilename)
641  else:
642  self.__myImage = Image.new("RGB", (self.width, self.height))
643  #The results of calling asarray() on the now created Image object is a 4-dimensional
644  # array. The original array is made up of rows, each row is made up of pixels, and
645  # each pixel is a set of 3 values (R,G,B of the pixel)
646  tmpPixels = asarray(self.__myImage)
647  self.pixels = []
648  #Now we are going to mirror the structure of the return of asarray(), only each
649  # pixels will now be an object
650  rowCount = 0
651  for row in tmpPixels:
652  self.pixels.append([])
653  colCount = 0
654  for pixel in row:
655  pixel = tuple([int(val) for val in list(pixel)])
656  coord = (colCount, rowCount)
657  self.pixels[-1].append( rgbPixel(pixel,coord) )
658  colCount+=1
659  #END FOR
660  rowCount+=1
661  #END FOR
662 
663  #Setting some other useful variables for users
664  self.width, self.height = self.__myImage.size
665 
666  #Closing the file that we've opened
667  self.__myImage.close()
668  #END DEF
669 
670  def __as_array(self):
671  """
672  Converts the 3-dimensional array of pixel objects into a 4-dimensional
673  array of RGB values (each pixel object is converted to an array)
674  """
675  tempArray = []
676  for row in self.pixels:
677  tempArray.append([])
678  for pixel in row:
679  init_arr = pixel._color_as_array()
680  append_arr = [uint8(rgbVal) for rgbVal in init_arr]
681  tempArray[-1].append(append_arr)
682  #END FOR
683  return tempArray
684  #END DEF
685 
686  def __str__(self):
687  """Returns a string representation of this object"""
688  shortInput = os.path.basename(self.inputFilename)
689  return ("RGB Image named {}\nSize is {}x{} pixels".format(shortInput, self.width, self.height)
690  )
691  #END DEF
692 #END CLASS ---------------------------------------------------------------------
def setColor(self, args)
Definition: simpleImage.py:206
def __le__(self, other)
Definition: simpleImage.py:368
def __isPixel(self, pixel)
Definition: simpleImage.py:146
def __init__(self, args)
Definition: simpleImage.py:60
def _color_as_tuple(self)
Definition: simpleImage.py:275
def __eq__(self, other)
Definition: simpleImage.py:355
def getAllPixels(self)
Definition: simpleImage.py:537
def cdist(self, kwargs)
Definition: simpleImage.py:290
def __setCoord(self, args)
Definition: simpleImage.py:238
def __setX(self, coord)
Definition: simpleImage.py:192
def _coord_as_tuple(self)
Definition: simpleImage.py:283
def setBlue(self, val)
Definition: simpleImage.py:183
def getPixel(self, args)
Definition: simpleImage.py:514
def setGreen(self, val)
Definition: simpleImage.py:175
def setRed(self, val)
Definition: simpleImage.py:167
def setName(self, name)
Definition: simpleImage.py:546
def ldist(self, pixel)
Definition: simpleImage.py:319
def __isColor(self, color)
Definition: simpleImage.py:153
def __lt__(self, other)
Definition: simpleImage.py:348
def __gt__(self, other)
Definition: simpleImage.py:362
def __setY(self, coord)
Definition: simpleImage.py:199