Flipping Images - python

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);

Related

Why isn't Python writing elements from one 3-D list to another?

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)]

How to get instance objects to change position horizontally

I'm creating an instance python command where the primary purpose is to generate objects in neat horizontal rows. Even though I can randomize rotation and set the range, I still can't figure out how to get the objects to appear in horizontal rows.
I already tried to use the xform command to get the objects to move along the x coordinates.
import maya.cmds as MC
import random as RN
def ChainmailGenerator():
thing = MC.ls(sl=True)
print thing
if not thing:
MC.error (" ***Error - you need to select an object *** ")
# create a group node
grp = MC.group(empty=True, name=thing[0] + '_grp#')
#Loop though the items below with the range of a
for i in range (0,25):
instanceObj = MC.instance(thing, name=thing[0]+'instance#', smartTransform=True)
rx = RN.uniform(-1,1)*5
ry = RN.uniform(-1,1)*5
rz = RN.uniform(-1,1)*5
MC.rotate (rx,ry,rz, instanceObj)
MC.xform (r=True, ro=(90, 0, 0) )
tx = 5
MC.xform ( instanceObj, t=(0,15+1,0))
MC.parent (instanceObj,grp)
print "*** chainmail ***"
ChainmailGenerator()
The expectations are for the objects to generate in horizontal increments as if they're forming neat rows.
here is an example to create 10 spheres along x, moving them with xform :
step = 1
tx = 0
for x in range(10):
sphere = cmds.polySphere()[0]
cmds.xform(sphere, t=[tx,0,0])
tx+= step
The reason yours is placing everything in the same place now is because you aren't multiplying it against a value that keeps increasing. Normally you could hard-code some random value to space each one out, but this would yield inconsistent results.
Here's a generic way to go about it that seems to work with any object.
The idea is to use the mesh's bounding box to determine what the spacing should be by looking at the size of its x axis. You can also move it in place with xform, but you do need to include its worldspace parameter so that it doesn't move it relative to its current position.
import maya.cmds as cmds
def cloneInRow(count):
# Get selection.
thing = cmds.ls(sl=True)
if not thing:
cmds.error("You need to select an object")
# Get selection's mesh shape.
mesh = cmds.listRelatives(thing[0], shapes=True, f=True, ni=True, type="mesh")
if not mesh:
cmds.error("Unable to find a mesh on the selected object")
# Determine spacing by looking at object's bounding box. Use its `x` axis size.
bb_min = cmds.getAttr(mesh[0] + ".boundingBoxMin")[0]
bb_max = cmds.getAttr(mesh[0] + ".boundingBoxMax")[0]
spacing = bb_max[0] - bb_min[0]
# Create a root transform to parent to.
grp = cmds.group(empty=True, name=thing[0] + '_grp#')
# Create instance, and move it in place.
for i in range (0, count):
instanceObj = cmds.instance(thing[0], name=thing[0] + 'instance' + str(i), smartTransform=True)
cmds.xform(instanceObj, ws=True, t=(i * spacing, 0, 0))
cmds.parent(instanceObj, grp)
cmds.select(grp)
cloneInRow(10)
With this I can take this crossbow:
And clone any of its objects and get nice spacing:
The only catch is rotation. If your pivot isn't centered to the mesh, then randomizing its rotation will lose its place in space (since rotating would also effects its position!) So if you got weird pivots then it won't look nice when you add back on rotations.

Python: Log pixel-intensity values from a video-file

I read in a video as a grayscale. I mark a circular contour on each frame of the video and try to record the intensity of each pixel on this contour:
frameCount += 1
currentTimestamp = timestamp + frameCount/fps
file = open("myRecord.txt", "a+") #a+ to append to file
file.write(str(currentTimestamp))
circleSegment = get_circle_segment(center_pt, 60, 360) #this function returns a list of tuples, where each tuple (x,y) contains x- and y-coordinates of a point along the circular contour
for el in circleSegment:
pixel = gray_frame[el[1],el[0]]
#log information
file.write('\t' + str(pixel))
file.write('\n')
file.close()
This works perfectly for a circular arch of up to 180° and even beyond (I have tested with 181° as well). However, as soon as I go beyond a certain value, e.g. 270° or 359° or 360° (a complete circle, which is what I want), the data logged simply contains chinese characters.
I have no idea what may cause this issue! Can it be that as soon as my txt-file goes beyond 1,000 KB something goes wrong?
after comments, this code works:
frameCount += 1
currentTimestamp = timestamp + frameCount/fps
storeData = []
storeData.append(currentTimestamp)
circleSegment = get_circle_segment(center_pt, 60, 360)
for el in circleSegment:
pixel = gray_frame[el[1],el[0]]
storeData.append(pixel)
#log information
file = open("myRecord.txt", mode="a+") #a+ to append to file
for element in storeData:
file.write('\t' + str(element))
file.write('\n')
file.close ()

How to get RGB color codes from an image and making them array?

Before asking a question I want to show my Python code which I'm using for reading Excel cells where I'm drawing whatever I want and preparing Excel cells for arduino codes to light on 32x92pixel tower.
import xlrd
ExcelFileName= 'data.xlsx'
workbook = xlrd.open_workbook(ExcelFileName)
worksheet = workbook.sheet_by_name("Sheet1")
#inside of the data.xlsx file I create 39 row and 31 column and this where I'm #drawing what I want to light up.
num_rows = worksheet.nrows #Number of Rows
num_cols = worksheet.ncols #Number of Columns
x=0 #for x line
y=0 #for y line
floors=-1
rooms=-1
def cell(y,x): #for getting values from each cells
return worksheet.cell_value(y, x) # Read the data in the current cell
#this is tower model and every floor has 92 addressable rgb leds.And there is 30 floors.Each 10 floor has different data cable so 1 data line controls 920 rgb.
#On arduino I have macro which is "#define LEDNO(FLOOR, ROOM) ((ROOM) + (FLOOR*92))" this.So if I write floor and room number I will get number of led.Because all strip connect to each other.
#First floor data connected from "right bottom corner",this is my start point for leds.And I'm reading excel cells so I have to start to reading from right bottom corner to have in order for my leds.
for i in range(30,20,-1):
y=i
floors+=1
for j in range(38,0,-1):
x=j
rooms+=1
if rooms>37:
rooms=0
print("leds[LEDNO(" + str(floors) + "," + str(rooms) +")].setRGB(" + str(cell(y,x)) + ");")
#for next 10 floor rooms should start from -1 again.
rooms=-1
for i in range(20,10,-1):
y=i
floors+=1
for j in range(38,0,-1):
x=j
rooms+=1
if rooms>37:
rooms=0
print("leds2[LEDNO(" + str(floors) + "," + str(rooms) +")].setRGB(" + str(cell(y,x)) + ");")
rooms=-1
for i in range(10,0,-1):
y=i
floors+=1
for j in range(38,0,-1):
x=j
rooms+=1
if rooms>37:
rooms=0
print("leds3[LEDNO(" + str(floors) + "," + str(rooms) +")].setRGB(" + str(cell(y,x)) + ");")
Question: How can I get RGB codes from an image with Python? And is it possible to convert this RGB codes for 30x92 pixel? Atleast I need to read and print given image pixels.
I need to get RGB color codes from image same as what I did on excel.I don't know if its possible with Python or not.
You may want to use scipy to read the image as a (30,92,3) numpy array (possibly ,4 for the last one, depending on the file that you are not showing), then assign the value as you want:
img[i, j, :] = RGBvalue

Horizontal flip of Image on Python with pypng

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]

Categories