I have multiple lists (bit0, bit1, bit2, etc), and I want to iterate through them in a loop like this:
for i in range(3):
view_image(bit1[i])
view_image(bit2[i])
view_image(bit3[i])
view_image(bit4[i])
view_image(bit5[i])
How can I avoid repetition of the view_image statements by constructing the name of its argument inside the loop?
Something like this: view_image('bit' + str(i+1)[i])
These lists represent collections of images as numpy arrays. So for example, bit1[0] is the first image in the bit1 collection.
I would it do like
arrays = (bit1, bit2, bit3, bit4, bit5)
for i in range(3):
for arr in arrays:
view_image(arr[i])
As you are storing several images of different resolution, and have a maybe runtime-determined number, you can resort to the following:
For creation of the list, you can have:
resolutions = ('low', 'medium', 'high', 'hq')
images = {} # empty dict
for res in resolutions:
if pic_present(res):
images[res] = get_image(res)
# Now you can proceed as above:
for i in range(3):
for res, img in images.iteritems():
announce(res, i) # sth. like print "Showing component", i, "of image", res
view_image(img[i])
If the number of images is always the same, you can use a list:
images = [] # empty list
for index, res in enumerate(resolutions):
images[index] = get_image(res)
# Now you can proceed as above:
for i in range(3):
for index, img in enumerate(images):
announce(resolutions[index], i) # sth. like print "Showing component", i, "of image", res
view_image(img[i])
The same thing a little bit different, if you have 256 levels of resolution, unnamed. In this case, you just replace
resolutions = ('low', 'medium', 'high', 'hq')
with
resolutions = range(256) # gives you the numbers of 0 to 255 to iterate over
and proceed as above. No need to hassle aroung with 256 different variable names, but the data is neatly put into a dict or list and that's it.
If possible, at the beginning set it up as
bit = [[],[],[],[],[]]
and build up each part of bit. Then you can loop through as
for i in xrange(3):
for bitarray in bit:
view_image(bitarray)
If they really can't be put into a list of lists that you can enumerate over, then what glglgl suggested looks best.
This is the answer I was looking for (thanks to tmrlvi):
for i in range(3):
for k in range(16):
view_image(locals()['bit'+str(k+1)][i])
I looked at this blog post:
http://stupidpythonideas.blogspot.de/2013/05/why-you-dont-want-to-dynamically-create.html
but I still don't understand why this code would be a bad idea in my case.
If you can explain what can go wrong, I would appreciate it!
Related
This question already has an answer here:
fastest way to find the rgb pixel color count of image
(1 answer)
Closed last month.
I want to know the list of most used colors in this picture:
I tried the following code, but it takes too long:
from PIL import Image
colors = []
class Color:
def __init__(self, m, c):
self.col = c
self.many = m
im = Image.open("~/.../strowberry.jpeg")
def cool():
for i in im.getdata():
i = str(i)
i = i.replace(", ", "")
i = i.replace("(", "")
i = i.replace(")", "")
i = int(i)
colors.append(Color(1, i))
for x in colors:
num = 0
for j in range(len(colors)):
if x.col == colors[num].col:
del colors[num]
num -= 1
x.many += 1
num += 1
for obj in colors:
print(obj.many, obj.col)
cool()
Why is the code so slow and how can I improve the performance?
Do not reinvent the wheel. The Python Standard Library contains a Counter that can do this for you much more efficiently. Using this, you don't need to iterate over the data yourself. You also do not need to define a Class and perform the string operations. The code is very short and simple:
import collections
from PIL import Image
im = Image.open('strawberry.jpg')
counter = collections.Counter(im.getdata())
for color in counter:
print(f'{counter[color]} times color {color}')
If you really need the Color objects (for whatever you want to do with it later in your program), you can easily create this from the counter object using this one-liner:
colors = [Color(counter[color], color) for color in counter]
...and if you really need it in the same string format as in your original code, use this instead:
colors = [Color(counter[color], int(''.join(map(str, color)))) for color in counter]
Note that the two one-liners make use of list comprehension, which is very Pythonic and in many cases very fast as well.
The code int(''.join(map(str, color))) does the same as your 5 lines of code in the inner loop. This uses the fact that the original data is a tuple of integers, which can be converted to strings using map(str, ...) and then concatenated together using ''.join(...).
All this together took about 0.5 second on my machine, without the printing (which is slow anyway).
I'm trying to create a program that creates an average out of an image by looping through each RGBA value in two images to average them out and create one composite image, but the right value isn't being written to my list comp_img, which contains all the new image data.
I'm using these 256x256 images for debugging.
But it just creates this as output:
While this is the composite color, the 64 gets wiped out entirely. Help is very much appreciated, thank you.
from PIL import Image
import numpy as np
from math import ceil
from time import sleep
red = Image.open("64red.png")
grn = Image.open("64green.png")
comp_img = []
temp = [0,0,0,0] #temp list used for appending
#temp is a blank pixel template
for i in range(red.width):
comp_img.append(temp)
temp = comp_img
#temp should now be a row template composed of pixel templates
#2d to 3d array code go here
comp_img = []
for i in range(red.height):
comp_img.append(temp)
reddata = np.asarray(red)
grndata = np.asarray(grn)
reddata = reddata.tolist() #its uncanny how easy it is
grndata = grndata.tolist()
for row, elm in enumerate(reddata):
for pxl, subelm in enumerate(elm):
for vlu_index, vlu in enumerate(subelm):
comp_img[row][pxl][vlu_index] = ceil((reddata[row][pxl][vlu_index] + grndata[row][pxl][vlu_index])/2)
#These print statements dramatically slowdown the otherwise remarkably quick program, and are solely for debugging/demonstration.
output = np.array(comp_img, dtype=np.uint8) #the ostensible troublemaker
outputImg = Image.fromarray(output)
outputImg.save("output.png")
You could simply do
comp_img = np.ceil((reddata + grndata) / 2)
This gives me
To get correct values it needs to work with 16bit values - because for uint8 it works only with values 0..255 and 255+255 gives 254 instead of 510 (it calculates it modulo 256 and (255+255) % 256 gives 254)
reddata = np.asarray(red, dtype=np.uint16)
grndata = np.asarray(grn, dtype=np.uint16)
and then it gives
from PIL import Image
import numpy as np
red = Image.open("64red.png")
grn = Image.open("64green.png")
reddata = np.asarray(red, dtype=np.uint16)
grndata = np.asarray(grn, dtype=np.uint16)
#print(reddata[128,128])
#print(grndata[128,128])
#comp_img = (reddata + grndata) // 2
comp_img = np.ceil((reddata + grndata) / 2)
#print(comp_img[128,128])
output = np.array(comp_img, dtype=np.uint8)
outputImg = Image.fromarray(output)
outputImg.save("output.png")
You really should work with just NumPy arrays and functions, but I'll explain the bug here. It's rooted in how you make the nested list. Let's look at the first level:
temp = [0,0,0,0] #temp list used for appending
#temp is a blank pixel template
for i in range(red.width):
comp_img.append(temp)
At this stage, comp_img is a list with size red.width whose every element/pixel references the same RGBA-list [0,0,0,0]. I don't just mean the values are the same, it's one RGBA-list object. When you edit the values of that one RGBA-list, you edit all the pixels' colors at once.
Just fixing that step isn't enough. You also make the same error in the next step of expanding comp_img to a 2D matrix of RGBA-lists; every row is the same list.
If you really want to make a blank comp_img first, you should just make a NumPy array of a numerical scalar dtype; there you can guarantee every element is independent:
comp_img = np.zeros((red.height, red.width, 4), dtype = np.uint8)
If you really want a nested list, you have to properly instantiate (make new) lists at every level for them to be independent. A list comprehension is easy to write:
comp_img = [[[0,0,0,0] for i in range(red.width)] for j in range(red.height)]
I want to generate a number of note in a music database, but I don't know how to use some iteration like for loop to simplify the following, I hope someone can shed some lights on me.
note1 = note.Note(tune_array_bass[0])
note2 =note.Note(tune_array_bass[1])
note3 =note.Note(tune_array_bass[2])
note4 =note.Note(tune_array_bass[3])
note5 =note.Note(tune_array_bass[4])
note6 =note.Note(tune_array_bass[5])
note7 =note.Note(tune_array_bass[6])
note8 =note.Note(tune_array_bass[7])
note9 =note.Note(tune_array_bass[8])
note10 =note.Note(tune_array_bass[9])
note11 =note.Note(tune_array_bass[10])
note12 =note.Note(tune_array_bass[11])
note13 =note.Note(tune_array_bass[12])
note14 =note.Note(tune_array_bass[13])
I didn't exactly understand what you meant but, here's my solution to list the notes using a loop:
class Note:
def __init__(self,note):
self.note = note
tune_array_bass = [1,2,3,4,5,6,7,8,9,10,11,12,13]
for item in tune_array_bass:
new_notes = Note(item)
print(new_notes)
You could store the objects in a list instead:
notes = []
for i in range(14):
notes.append(note.Note(tune_array_bass[i]))
Then you can access each note object using notes[0], notes[1] etc.
Don't create many individual variables like this.
Instead, you could use a list:
notes = [note.Note(tune_array_bass[i]) for i in range(14)]
# get a note from the list:
notes[0]
Or a dictionary:
notes = {f"Note{i+1}":note.Note(tune_array_bass[i]) for i in range(14)}
# get a note from the dict:
notes["Note1"]
My problem is really quite simple.
I have a 100 images on my computer, those images are called 1.ppm 2.ppm and so on until 100.ppm
I want to read each image to a variable using imread, and then perform a few operations. I want to do the exact same thing to all of the images.
My question is this - Instead of copy pasting one hundred times, is it possible to use imread in a loop? something like:
for i in range(1,100):
X=io.imread('/home/oria/Desktop/more pics/'i'.ppm')
Instead of copy pasting the same code block and just changing the picture number a hundred times, I want to do this in a loop.
I have a similar issue with numpy.load. I want to load files called ICA1 ICA2 etc up to ICA100. Is it possible to write something like
numpy.load('/home/oria/Desktop/ICA DB/ICA'i'.npy)?
Like this:
for i in range(1,100):
X=io.imread('/home/oria/Desktop/more pics/%s.ppm' %(i))
Or, like this:
for i in range(1,100):
X=io.imread('/home/oria/Desktop/more pics/'+str(i)+'.ppm')
Go ahead and read the article on basic string operations as well as this simple article on string formatting
If I correctly understand what you're asking, it could be done as:
for i in range(1, 101):
x = io.imread('/home/oria/Desktop/more pics/' + str(i) + '.ppm')
Note that the high end of the range function is not inclusive, so using range(1, 100) would only produce 1, 2, 3...99. Also note that i must be converted to a string or you will receive TypeError: cannot concatenate 'str' and 'int' objects.
import cv2
import os
def load_images_from_folder(folder):
images = []
for filename in os.listdir(folder):
img = cv2.imread(os.path.join(folder,filename))
if img is not None:
images.append(img)
return images
Just use str.format, passing the variable i:
for i in range(1,100):
X = io.imread('/home/oria/Desktop/more pics/{}.ppm'.format(i))
When you want to load with numpy do the same thing again:
for i in range(1,100):
X = numpy.load('/home/oria/Desktop/ICA DB/ICA{}.npy'.format(i))
I am trying to horizontally flip an image (from left to right) on Python using PyPNG, I have written the following codes but it does not seem to work, anybody have any idea what I'm doing wrong here?
def horizontal_flip(image):
rows = len(image)
cols = len(image[0])
new_image = []
for r in range(rows):
new_row = []
for c in range(0,cols,3):
if c != cols/2:
image[c:c+3], image[-c-3: -c] = image[-c-3: -c], image[c:c+3]
new_row.append(image[r][c])
new_image.append(new_row)
return new_image
new_row.append(image[r][c]) should be outside of the if.
Also, you're flipping the image horizontally... twice. Make your for loop use range(0,cols/2,3). (That may also eliminate the need for that if.)
You're also modifying the original image in-place; are you sure you want to do that?
Seems a simpler solution might be to loop through each row in reverse, appending to a row for the new image.
The inner loop logic is wrong but particularly, this line:
image[c:c+3], image[-c-3: -c] = image[-c-3: -c], image[c:c+3]
You are changing the image variable in-place, but you seemed to forget the row variable r. So right now, you are changing rows. And your negative slicing is a bit off. For c=0, you'll get image[-3:0] and this is not a valid slice and it will return [].
But judging from your code you don't mean to change image in-place, you rather want to create new_image. What you should be doing is inserting slices at the end of new_row:
def horizontal_flip(image):
rows = len(image)
cols = len(image[0])
new_image = []
for r in range(rows):
new_row = []
for c in range(0,cols,3):
new_row = image[r][c:c+3] + new_row
new_image.append(new_row)
return new_image
By the way, you can also change the image in-place, but be careful. As you are passing a list, you should copy it before changing so that original is unchanged. Here is that version:
def horizontal_flip(image):
cols = len(image[0])/3
#make a copy so that original image is not altered
image = [row[:] for row in image]
for row in image:
for c in range(int(cols/2)): # int() is not needed for Python 2.x, since integer division yields integer
# This also takes care of odd n cases, middle chunk is not changed.
row[3*c:3*c+3], row[3*(cols-c-1):3*(cols-c-1)+3] = row[3*(cols-c-1):3*(cols-c-1)+3], row[3*c:3*c+3]
return image
This can also be done with a list comprehension in single line, but it will be less readable. If you like, here is how you can do it:
from itertools import chain
flipped_image = [list(chain(*[row[3*i:3*i+3] for i in range(len(image[0])/3-1,-1,-1)])) for row in image]