unsubscriptable object - python

I'm using PIL
im = Image.open(teh_file)
if im:
colors = im.resize( (1,1), Image.ANTIALIAS).getpixel((0,0)) # simple way to get average color
red = colors[0] # and so on, some operations on color data
The problem is, on a few (very few, particulary don't know why those exactly, simple jpegs) I get 'unsubscriptable object' on line "colors[0]". Tried:
if colors:
gets true and goes on.
if len(colors):
gives 'len() of unsized object'
What condition should I apply not to get this exception?
What's the cause of the problem?

From the PIL docs:
getpixel
im.getpixel(xy) => value or tuple
Returns the pixel at the given position. If the image is a multi-layer image, this method returns a tuple.
So it seems that some of your images are multilayer, and some are single-layer.

As noted in another answer, getpixel returns either a single value, or a tuple. You could check the type and do the appropriate action in the following ways:
if isinstance(colors, tuple):
color = colors[0]
else:
color = colors
# Do other stuff
or:
try:
color = colors[0]
except: # Whatever the exception is - IndexError or whatever
color = colors
# Do other stuff
The second way is probably more Pythonic.

Ok the case was, that when B&W images have no RGB band (L band), it returns an integer with the pixel color single value, not a list of rgb values. The solution is to check bands
im.getbands()
or the simpler for my needs was:
if isinstance(colors, tuple):
values = {'r':colors[0], 'g':colors[1], 'b':colors[2]}
else:
values = {'r':colors, 'g':colors, 'b':colors}

Related

Multiple lists in a for loop, numpy shape command requiring list entry?

I created the following for loop
for ItemTemplate in ItemTemplates:
x = 0
self.needle_images.append(cv.imread(ItemTemplate, cv.IMREAD_UNCHANGED))
self.needle_widths.append(self.needle_images[x].shape[1])
self.needle_heights.append(self.needle_images[x].shape[0])
x = x+1
I originally tried to write the for loop like this:
for ItemTemplate in ItemTemplates:
self.needle_images.append(cv.imread(ItemTemplate, cv.IMREAD_UNCHANGED))
self.needle_widths.append(self.needle_images.shape[1])
self.needle_heights.append(self.needle_images.shape[0])
I was assuming I didn't need to add the list entries to this code and perhaps there is a better way to do this but my Python skills are very young. The top example is fine and my code runs ok with it, but I am looking to see if there was a better way to accomplish this task.
I don't know why you need to store the image, width and height in three lists since image already has the width and height as its shape member, which you are already using. I also wonder why your top example works, since x will always stay 0 and this way all the upcoming Images will have the size of the first Image. The second example clearly doesn't work since you try to take the shape member of an array.
Changes I would make
if you need the index (x in your case) I would use the enumerate() function to get it:
for x, ItemTemplate in enumerate(ItemTemplates):
# x : index starting from 0
...
you could also first get the image into a local variable and then add it:
for ItemTemplate in ItemTemplates:
img = cv.imread(ItemTemplate, cv.IMREAD_UNCHANGED)
self.needle_images.append(img)
self.needle_widths.append(img.shape[0]) # if needing to store widths in own array
...
if you don't want to get the width and height from the shape member you could store the Image in an own class or dict, in two separate members width and height though this wouldn't make a big difference from just using the shape.
So if the for loop doesn't get more functionality, I would write:
for ItemTemplate in ItemTemplates:
self.needle_images.append(cv.imread(ItemTemplate, cv.IMREAD_UNCHANGED))
and use the shape member for the size since you can always store the shape value in a local variable when using it later:
for img in self.needle_images:
width, height, channels = img.shape
display.blit(img, (width, height)) # pseudo operation
...
The last two lines of the loop are always using the last image appended to needle_images. The index of the last item in a list is -1.
for ItemTemplate in ItemTemplates:
self.needle_images.append(cv.imread(ItemTemplate, cv.IMREAD_UNCHANGED))
self.needle_widths.append(self.needle_images[-1].shape[1])
self.needle_heights.append(self.needle_images[-1].shape[0])
Or just bite the bullet and assign the image to a name at the top of the loop.
for ItemTemplate in ItemTemplates:
temp = cv.imread(ItemTemplate, cv.IMREAD_UNCHANGED)
self.needle_images.append(temp)
self.needle_widths.append(temp.shape[1])
self.needle_heights.append(temp.shape[0])
Or even...
for ItemTemplate in ItemTemplates:
temp = cv.imread(ItemTemplate, cv.IMREAD_UNCHANGED)
w,h = temp.shape
self.needle_images.append(temp)
self.needle_widths.append(w)
self.needle_heights.append(h)

Can morphology.remove_small_objects remove big objects?

I was wondering if morphology.remove_small_objects could be used to remove big objects. I am using this tool to detect the objects as seen in the figure.
,
However,there are big objects as seen in the left. Is there any way I could use morphology.remove_small_objects as a threshold, for example:
mask=morphology.remove_small_objects(maske, 30)
Could I use like a range? between 30 and 200 so I can ignore the red detection in the image.
Otherwise, I will just count the white pixels in the image and remove the ones that have the highest.
This might be a good contribution to the scikit-image library itself, but for now, you need to roll your own. As suggested by Christoph, you can subtract the result of remove_small_objects from the original image to remove large objects. So, something like:
def filter_objects_by_size(label_image, min_size=0, max_size=None):
small_removed = remove_small_objects(label_image, min_size)
if max_size is not None:
mid_removed = remove_small_objects(small_removed, max_size)
large_removed = small_removed - mid_removed
return large_removed
else:
return small_removed

Tkinter askcolor returning wrong RGB values?

I am using Python 3.6.1.
I need to get a string (or int values) of RGB values of a color chosen with Tkinter askcolor method.
My code:
from tkinter.colorchooser import askcolor
color = askcolor()
rgb_tuple = color[0] #gets tuple of RGB values
color_result_rgb = ' '.join(format(x, "1.0f") for x in rgb_tuple) #tuple into a string
Now, say i choose a pink color with values (255, 0, 128)
>>> rgb_tuple
returns:
(255.99609375, 0.0, 128.5)
And
>>> color_result_rgb
returns:
'256 0 128'
How do I fix this so the returned values are correct?
This appears to be a bug in tkinter1. askcolor should be returning integers. The fix seems simple: convert the values to integers before converting them to a string:
color_result_rgb = ' '.join(str(int(x)) for x in rgb_tuple)
1 Looking at the tkinter code, it is indeed intentionally returning a floating point number even though the underlying tcl/tk interpreter is returning an int. This seems like a side effect of the change to the behavior of the / operator in python 3.x
This is what the tkinter code is doing to the raw value:
r, g, b = widget.winfo_rgb(result)
return (r/256, g/256, b/256), str(result)
I've submitted a bug report: askcolor is returning floats for r,g,b values instead of ints
I wanted to get the color from the color picker dialog to pass to the create_line method. The askcolor function/method returns ((255.99609375, 0.0, 0.0), '#ff0000'). It's the latter value I need, so these two lines did the trick for me...
rgb, color = askcolor()
canvas.create_line((lastx, lasty, event.x, event.y), fill=color)

finding out complementary/opposite color of a given color

I am trying to find out the complementary color of a given color using Python. here is my code. the code returns error message telling "AttributeError: 'list' object has no attribute 'join'" I need a hint. In addition, there might be a more robust code which calculates the opposite/complementary color, which I am basically looking for. your suggestions will be helpful.
from PIL import Image
def complementaryColor(hex):
"""Returns complementary RGB color
Example:
>>>complementaryColor('FFFFFF')
'000000'
"""
if hex[0] == '#':
hex = hex[1:]
rgb = (hex[0:2], hex[2:4], hex[4:6])
comp = ['02%X' % (255 - int(a, 16)) for a in rgb]
return comp.join()
another similar function
def blackwhite(my_hex):
"""Returns complementary RGB color
Example:
>>>complementaryColor('FFFFFF')
'000000'
"""
if my_hex[0] == '#':
my_hex = my_hex[1:]
rgb = (my_hex[0:2], my_hex[2:4], my_hex[4:6])
comp = ['%X' % (0 if (15 - int(a, 16)) <= 7 else 15) for a in rgb]
return ''.join(comp)
print blackwhite('#36190D')
Your join and formatting needed a fix. Lists do not have a join method, strings do:
def complementaryColor(my_hex):
"""Returns complementary RGB color
Example:
>>>complementaryColor('FFFFFF')
'000000'
"""
if my_hex[0] == '#':
my_hex = my_hex[1:]
rgb = (my_hex[0:2], my_hex[2:4], my_hex[4:6])
comp = ['%02X' % (255 - int(a, 16)) for a in rgb]
return ''.join(comp)
The formatting for hex shoud be %02X for two hex characters and not '02%X'. The later only appends a leading 02 to a mangled output of 3 characters instead of 6.
hex is builtin function, so you may consider changing the name to, say my_hex to avoid shadowing the original hex function.
You can use colorir's palettes for that:
>>> from colorir import SwatchPalette
>>> colors = SwatchPalette.new_complementary(2, color="ff0000")
>>> colors[1] # Get the complementary of red
HexRGB(#00ffff)
Of course you can pass any color you want to new_complementary's "color" parameter to get its complementary color.

how to properly read a PPM file using Python

Here is my overall instructions
Write a Color class that represents an RGB color using integer values in the range 0 to 255. Your class must:
Be placed in image.py
Provide a constructor that accepts the values of the red, green, and blue channels from the client and stores those values
Provide public methods that return the values of the red, green, and blue channels
Write a PortablePixmap class that represents a PPM image. Your class must:
Be placed in image.py
Provide a constructor that accepts the magic number, width, height, maximum color value, and pixel data from the client and stores those values
Store the pixel data as a list of (or list of lists of) Color objects
Provide a public method that returns a string representation of the PPM image
Write a read_ppm function that opens a PPM image file, reads its contents, and returns a PortablePixmap object that holds its contents. Your function must:
Be placed in image.py
Read the contents of a PPM image file
Not be sensitive to the formatting of the PPM image file
Exit with an error if the numbers of expected and provided pixels differ
Write a main function that tests your read_ppm function. Your function must be placed in main.py
this is what I have thus far
class Color:
# constructor takes in values from client and stores them
def __init__(self, red, green, blue):
# checks that type of arg == int: raises exception otherwise
if (isinstance(red, int) and isinstance(green, int) and isinstance(blue, int)):
print("good stuff, indeed integers")
else:
raise TypeError("Argument must be an integer.")
# checks if values are between 0 and 225
if red < 0 or red > 225:
print("0 < rgb values < 225")
elif green < 0 or green > 225:
print("0 < rgb values < 225")
elif blue < 0 or blue > 225:
print("0 < rgb values < 225")
# instance variables (RGB values)
self._red = red
self._green = green
self._blue = blue
# methods that reuturn RGB values
def returnRed(self):
return self._red
def returnGreen(self):
return self._green
def returnBlue(self):
return self._blue
'''class that represents a PPM image'''
class PortablePixmap:
def __init__(self, magic_number, width, height, max_color_value, pixel_data):
self._magic_number = magic_number
self._width = width
self._height = height
self._max_color_value = max_color_value
self._pixel_data = pixel_data
def __str__(self):
s = self._magic_number
s += '\n' + str(self._width)
s += ' ' + str(self._height)
s += '\n' + str(self._max_color_value)
for pixel in self._pixel_data:
s += ' ' + str(pixel[0])
s += ' ' + str(pixel[1])
s += ' ' + str(pixel[2])
return s
I have a few questions for clarification..
1. Did I go about creating the Color class correctly?
2. Do I even need to raise any exceptions in that class specifically? We will ultimately be reading from a file that contains everything in order but not necessarily on it's own individual line.
I really just want to know if I am going about this correctly. The instructions seem stepwise, but I am not really understanding how everything connects so I'm afraid I am either doing too much or too little.
Thanks in advance
It is not clear from the specification that you need to check the values, and your checks only raise exceptions in some cases, otherwise cause side effects (printing); from a reuse perspective, I'd prefer to have only the exceptions if any. Aside from the indentation error (which I assume is only here, not in your source) the Color class looks to cover the demands, although they are quite unpythonic with the accessors; probably someone was trained by Java.
The docstring should be inside the PortablePixmap class, not above it.
Most remarkable is the combination of demands that your class not be sensitive to the formatting of the PPM and store pixels as 8-bit unsigned RGB. This makes it impossible to support all PPMs, as they support 16-bit values (note the maxval field in the PPM format).
Your PortablePixmap class also doesn't use the Color class: "Store the pixel data as a list of (or list of lists of) Color objects". That requirement forces a rather awfully inefficient implementation, but the whole thing is an exercise, I suppose. You'll need to extract the RGB triplets from the pixel data string. That's also where you need the one check that is specified; verifying that there are exactly the right number of pixels. One would expect a ValueError exception if that fails.
If I were writing this sort of thing I might have used slots to reduce memory use for classes like Color, arrays to handle the large number of limited range numeric values, and possibly properties to make storage transparent without using unwieldy getter methods. split and join would make it easier to handle the collection of pixels.

Categories

Resources