Photo Processing With Cs1media - CS101 Introduction To Programming
Photo Processing With Cs1media - CS101 Introduction To Programming
Otfried Cheong
1 First steps with cs1media blue light intensities. Each intensity is an integer
between 0 (no light) and 255 (full light). So red is
Suppose you have a photo taken with your digital
(255, 0, 0), and yellow is (255, 255, 0).
camera on your computer. You can load this photo
using: Many colors are predefined and can be accessed
like this:
from cs1media import *
img = load_picture("C:/photos/geowi.jpg")
>>> Color.yellow
(If you do not remember the filename of the photo, (255, 255, 0)
you can instead simply say load_picture() and >>> Color.chocolate
use a file chooser to select your photo.) (210, 105, 30)
You have now created a picture object and as-
signed the name img to it. You can get some in-
teresting information about the picture using the To get a list of all predefined colors, use
methods size() (which returns a tuple consisting help("cs1media.Color") or simply help(Color)
of the width and height of the picture in pixels) and (if you have imported the cs1media module).
title() (which typically returns the filename). If
If you want to mix your own color, you can use:
you call the method show(), then Python will start
a program to display the picture:
>>> type(img) >>> choose_color()
<class ’cs1media.Picture’> (150, 76, 121)
>>> img.size()
(500, 375)
>>> img.title() choose_color() returns the color triple you have
’geowi.jpg’ chosen.
>>> img.show() cs1media also contains a tool that is useful to
find coordinates in a photograph (for instance to
select a range to crop the photo) or to find the
color of pixels you are interest in. You call it as
>>> picture_tool("C:/photos/geowi.jpg")
1
Even though it’s called “black and white”, we
def make_lighter(img, factor): have actually created graytone images. We can cre-
w, h = img.size() ate images really using only black and white if we
for y in range(h): threshold the image: if the luminance is larger than
for x in range(w): some threshold, we set the pixel to white, otherwise
r, g, b = img.get(x, y) to black:
r = int(factor * r)
g = int(factor * g)
b = int(factor * b)
if r > 255: r = 255
if g > 255: g = 255
if b > 255: b = 255
img.set(x, y, (r, g, b))
img = load_picture("photos/geowi.jpg")
make_lighter(img, 1.5)
img.show() 4 Reflections
When you look in a mirror, you see yourself mir-
You want to save a copy of the modified image?
rored. We can achieve this effect:
Just call
img.save_as("geowi_lighter.jpg") def mirror(img):
w, h = img.size()
for y in range(0, h):
When called as make_lighter(img, 0.5), the
for x in range(w / 2):
function actually makes the picture darker.
pl = img.get(x, y)
With the same principle, we can create various
pr = img.get(w - x - 1, y)
image effects. For instance, we can create the sen-
img.set(x, y, pr)
sation of a sunset by turning an image redder (by
img.set(w - x - 1, y, pl)
reducing the blue and green light intensity). We
can create a negative image if we replace (r,g,b)
by (255-r,255-g,255-b):
def luminance(p):
r, g, b = p
return int(0.299*r + 0.587*g + 0.114*b)
2
It is interesting to apply this technique to photos
of human faces, because faces are symmetric, but def blocks_rect(img, step, x1,y1, x2,y2):
not perfectly so, and the symmetrized version looks w, h = img.size()
interestingly different from the real face. for y in range(x1, x2 - step, step):
for x in range(y1, y2 - step, step):
# same as before
5 Pixelation
So far we modified every pixel of the photo inde-
pendently. Let’s see what we can do if we look at
several pixels together. For instance, we can take
the average color for a group of pixels:
3
7 Edge detection
The human visual system works by detecting
boundaries. Even though natural objects like hu-
man faces have no sharp boundaries, we can easily
recognize a child’s line drawing of a face. When
teaching a computer to recognize objects—an area
called computer vision—one of the first steps is to
recognize edges in the image.
A simple edge detection algorithm is the follow-
ing: we compare the luminances of a pixel and the
And now an entirely artificial image:
pixels to its left and above. If there is a big enough
difference in both directions, then we have found an
“edge” pixel. We can visualize this easily by draw- def interpolate(t, c1, c2):
ing edge pixels black, and all other pixels white: r1, g1, b1 = c1
r2, g2, b2 = c2
def edge_detect(img, threshold): r = int((1-t) * r1 + t * r2)
w, h = img.size() g = int((1-t) * g1 + t * g2)
r = create_picture(w, h) b = int((1-t) * b1 + t * b2)
for y in range(1, h-1): return (r, g, b)
for x in range(1, w-1):
pl = luminance(img.get(x-1,y)) def gradient(img, c1, c2):
pu = luminance(img.get(x,y-1)) w, h = img.size()
p = luminance(img.get(x,y)) for y in range(h):
if (abs(pl - p) > threshold and t = float(y) / (h-1) # 0 .. 1
abs(pu - p) > threshold): p = interpolate(t, c1, c2)
r.set(x, y, Color.black) for x in range(w):
else: img.set(x, y, p)
r.set(x, y, Color.white)
return r img = create_picture(100, 100)
gradient(img, Color.yellow, Color.red)
4
It turns out our statue actually has two frames, a
def crop(img, x1, y1, x2, y2): black outer frame and a whitish inner frame:
w1 = x2 - x1
h1 = y2 - y1 r1 = load_picture("photos/statue.jpg")
r = create_picture(w1, h1) r2 = autocrop(r1, 60)
for y in range(h1): r3 = autocrop(r2, 200)
for x in range(w1):
r.set(x, y, img.get(x1+x, y1+y))
return r
img = load_picture("photos/geowi.jpg")
r = crop(img, 67, 160, 237, 305)
5
def scale_down(img, factor): def collage1():
w, h = img.size() trees = load_picture("trees1.jpg")
w /= factor r1 = load_picture("statue.jpg")
h /= factor r2 = scale_down(r1, 2)
result = create_picture(w, h) mirror(r2)
norm = factor ** 2 paste(trees, r2, 100, 20)
for y in range(h): trees.show()
for x in range(w):
# compute average
This works, but the result is not too exciting:
r, g, b = 0, 0, 0
x1 = x * factor
y1 = y * factor
for x0 in range(factor):
for y0 in range(factor):
r0,g0,b0 = img.get(x1+x0,y1+y0)
r, g, b = r+r0, g+g0, b+b0
r, g, b = r/norm, g/norm, b/norm
result.set(x, y, (r, g, b))
return result
6
Now all we need is an improved version of the 14 Concatenating photos
paste function that will skip background pixels
Let’s make a comic strip out of a list of given pic-
when copying an image onto the canvas. We need
tures. We first have to compute the size of the
to pass the color key which we used for the back-
result—its width is the sum of the width of the
ground to the function:
given pictures, its height is the maximum height of
def chroma_paste(canvas,img,x1,y1,key): any picture. We then create a new picture of the
w, h = img.size() correct size, and and copy the images at the right
for y in range(h): location one by one:
for x in range(w):
def concat(imglist):
p = img.get(x, y)
# compute height and width
if p != key:
w = 0
canvas.set(x1 + x, y1 + y, p)
h = 0
for img in imglist:
Let’s try this on our example: w1, h1 = img.size()
w += w1
def collage2(): h = max(h, h1)
trees = load_picture("trees1.jpg") r = create_picture(w, h, Color.white)
r1 = load_picture("statue.jpg") x0 = 0
r2 = scale_down(r1, 2) for img in imglist:
mirror(r2) w1, h1 = img.size()
chroma_paste(trees, r2, 100, 20, for y in range(h1):
Color.yellow) for x in range(w1):
trees.show() r.set(x0 + x, y, img.get(x, y))
x0 += w1
return r