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]
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).
How to crop array into specific size, in here 32x32. from left top to right? is there built-in to this ways? i read somewhere implement manual ways, make numpy slower. so i think can improve this manipulation.
import numpy
frame = numpy.full((1080, 1920), 255)
frameBlocks = numpy.array()
wBlock = 32
hBlock = 32
# Determine total block each row because sometime the latest block not exactly fit size.
wBlockLength = numpy.floor(frame.shape[1] / wBlock)
hBlockLength = numpy.floor(frame.shape[0] / hBlock)
# Crop from left to right with 32 x 32
for hBL in range(0, hBlockLength):
hIndex = hBL*hBlock
for wBL in range(0, wBlockLength):
block = numpy.zeros(wBlock, hBlock)
wIndex = wBL*wBlock
for hB in range(0, hBlock):
for wB in range(0, wBlock):
block[hB][wB] = frame[hIndex][wIndex]
wIndex += 1
hIndex += 1
frameBlocks.append(block)
# create index order to shuffle
blocksLength = len(frameBlocks)
index = numpy.array(range(0, blocksLength))
# Shuffle order by index
frameBlocks[index]
my other question how to turn blocks that already suffle into frame? exactly it simple just repeat that loop again. but, how about in numpy ways?
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 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!
I am trying to complete an image editing task in my learning Python book. I need help with the horizontal flip.
The instructions are: Write a function called "flip_horizontal" which will flip the picture horizontally. That is, the pixel that is on the far right end of the row ends up on the far left of the row and vice versa (remember to preserve RGB order!).
My code does not flip the image horizontally when I open it. Also, how can I write my effects to different files (use the original file and apply one function the original file and output it, and then apply another function to the original file and output it to another file). Please, keep in mind that I am only 11 years old and have a very basic understanding of Python and image editing, it is just an interest of mine.
class PPM(object):
def __init__(self, infile, outfile):
self.infile=infile
self.outfile=outfile
#Read in data of image
data= open(self.infile,"r")
datain=data.read()
splits=datain.split()
#Header info
self.type=splits[0]
self.columns=splits[1]
self.row=splits[2]
self.colour=splits[3]
self.pixels=splits[4:]
def negate_red(self):
for b in range (0, (len(self.pixels)) , 3):
remainder=255-self.colour
def writetofile(self):
dataout= open(self.outfile,"w")
dataout.write(self.type +"\n" + self.columns + "\n" + self.row +"\n"+ self.colour +"\n"+ " ".join (self.pixels))
def grey_scale(self):
if int(self.columns) > 1000:
return "ERROR!! Number of columns is larger than what can be held in a buffer."
else:
for b in range(0, (len(self.pixels)) , 3):
sum = int(self.pixels[b]) + int(self.pixels[b+1]) + int(self.pixels[b+2])
avg = int(sum/3)
self.pixels[b] = str(avg)
self.pixels[b+1] = str(avg)
self.pixels[b+2] = str(avg)
def flatten_red(self):
for colour in range (0,len(self.pixels),3):
self.pixels [colour]=str(0)
#Create a 2d lists with the smaller lists containing the rgb values and append lists of lists
def horizontal_flip(self):
if int(self.columns) > 1000:
return "ERROR!! Number of columns is larger than what can be held in a buffer."
else:
temp_list = []
for b in range(int(self.row)):
column_list = []
column_list += self.pixels[0:int(self.columns) * 3]
self.pixels = self.pixels[int(self.columns) * 3 : ]
temp_list.append(column_list)
#print temp_list
new_list = []
for i in range(int(len(temp_list))):
new_list.append (temp_list[i][0])
new_list.append (temp_list[i][1])
new_list.append (temp_list[i][2])
temp_list[i] = temp_list[i][::-1]
sample= PPM("cake.ppm", "Replica.ppm")
sample.flatten_red()
sample.horizontal_flip()
sample.greyscale()
sample.negate_red()
Imagine a row of pixels.
i.imgur.com/1iZesZL.jpg
Now, what we want to do is to flip it so that the right-most pixel is on the left-most place, right?
So if we have the pixel on the far-left with the coordinates (x,y) then the pixel on the far-right has the coordinates (x+n, y) where n = the width of the picture in pixels.
i.imgur.com/EE7Qj5r.jpg
Now, a horizontal flip would look like this, right?
i.imgur.com/fbNLCuX.jpg
So what we do is we go from the far right and the far left, swap the values of the current pixels and go one step to the right and one step to the left until they meet in the middle.
In pseudo-code this might look something like this:
n = width
x = 0
y = whatever row we're on currently
while n != width/2
temporary = (x,y) # (x,y) refers to a specific pixel in the picture
(x,y) = (n, y)
(n, y) = temporary
n = n-1
x = x+1
Do you think that's enough to solve the rest yourself? Wouldn't want to take the fun out of it :-)
Are you really 11 years old?
It looks like each element of your temp_list is a column of the image to reverse the order of the columns you just have to do temp_list = temp_list[::-1], but you're doing temp_list[i] = temp_list[i][::-1] which I think flips the image up-down (I might have it backwards though). Either way, once you get the flip, you'll need to flatten the image again and replace self.pixels, something like:
pixels = []
for column in temp_list:
pixels.extend(column)
self.pixels = pixels
You're not doing much with new_list, I don't think you need it. Also if you want to save the image to different files, take the filename as an argument to writetofile, if you do that you won't need to have it in __init__, so something like:
class PPM(object):
def __init__(self, infile):
self.infile=infile
# More code here
def writetofile(self, outfile):
dataout = open(outfile,"w")
dataout.write(self.type +"\n" + self.columns + "\n" + self.row +"\n"+ self.colour +"\n"+ " ".join (self.pixels))
dataout.close()
sample = PPM("cake.ppm")
sample.horizontal_flip()
sample.writetofile("Replica1.ppm")
sample.negate_red()
sample.writetofile("Replica2.ppm")
Maybe not for you since you want to practice but I came here looking for a solution to the same Problem after Research I found this and wanted to share the following:
OpenCV provides a function to flip an image.
void flip(array src, array dst, int flipCode)
Flips a 2D array around vertical, horizontal or both axes.
Parameters:
src – The source Array
dst – The destination array; will have the same size and same type as src
flipCode – Specifies how to flip the array: 0 means flipping around the x-axis, positive (e.g., 1) means flipping around y-axis, and negative (e.g., -1) means flipping around both axes.The function flip flips the array in one of three different ways (row and column indices are 0-based).
Example code:
cv.flip(original_image,flip_image,1);