"""
Implementation of Liquid Resizing in Python.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Copyright (C) 2007 Yew Jin, Lim
Things needed to improve:
1. Use ImageFilter instead of computing intensity gradient manually
2. Do not maipulate image with PIL directly (?)
3. Use scipy.weave to write tight inner loop
"""
from PIL import Image, ImageEnhance
import numpy
import random
# geez, the things that get passed off as infinity these days
INFINITY = 99999
def createData(im, targetWidth, targetHeight):
width,height = im.size
height = max(height,targetHeight)
width = max(width,targetWidth)
working = Image.new("RGB",(width,height))
working.paste(im, (0,0,im.size[0],im.size[1]))
inten = numpy.zeros((width,height),numpy.int32)
xgradient = numpy.zeros((width,height),numpy.int32)
ygradient = numpy.zeros((width,height),numpy.int32)
energy = numpy.zeros((width,height),numpy.int32)
xmin = numpy.zeros((width,height),numpy.int32)
ymin = numpy.zeros((width,height),numpy.int32)
data = {}
data["originalImage"] = im
data["working"] = working
data["intensity"] = inten
data["targetHeight"] = targetHeight
data["targetWidth"] = targetWidth
data["xgradient"] = xgradient
data["ygradient"] = ygradient
data["energy"] = energy
data["xmin"] = xmin
data["ymin"] = ymin
return data
def init(data, width, height):
inten = data["intensity"]
im = data["working"]
#ImageEnhance.Sharpness(ImageEnhance.Color(im).enhance(2.0)).enhance(2.0).convert("L").show()
pix = ImageEnhance.Sharpness(ImageEnhance.Color(im).enhance(2.0)).enhance(2.0).convert("L").load()
for x in xrange(width):
for y in xrange(height):
inten[x,y] = pix[x,y]
xgradient = data["xgradient"]
xgradient[1:width-1,:height] = inten[2:width,:height] - inten[0:width-2,:height]
ygradient = data["ygradient"]
ygradient[:width,1:height-1] = inten[:width,2:height] - inten[:width,0:height-2]
energy = data["energy"]
energy[:width,:height] = numpy.abs(xgradient[:width,:height]) + numpy.abs(ygradient[:width,:height])
def resize(filename, targetWidth, targetHeight):
im = Image.open(filename)
width, height = im.size
data = createData(im, targetWidth, targetHeight)
working = data["working"]
workingPix = working.load()
while targetWidth != width or targetHeight != height:
working.show()
init(data, width, height)
energy = data["energy"]
minVal = INFINITY
minSeam = ""
if targetWidth != width:
xmin = data["xmin"]
xmin[1:width-1,1:height] = 0
xmin[1:width-1,0] = energy[1:width-1,0]
xmin[0,:height] = INFINITY
xmin[width-1,:height] = INFINITY
for y in xrange(height-1):
for x in xrange(width-2):
xmin[x+1,y+1] = energy[x+1,y+1] + numpy.min(xmin[x:x+3,y])
minIndex = numpy.argmin(xmin[1:width-1,height-1])
minSeam = "W"
minVal = xmin[minIndex+1,height-1]
if targetHeight != height:
ymin = data["ymin"]
ymin[1:width,1:height-1] = 0
ymin[0,1:height-1] = energy[0,1:height-1]
ymin[:width,0] = INFINITY
ymin[:width,height-1] = INFINITY
for x in xrange(width-1):
for y in xrange(height-2):
ymin[x+1,y+1] = energy[x+1,y+1] + numpy.min(ymin[x,y:y+3])
index = numpy.argmin(ymin[width-1,1:height-1])
val = ymin[width-1,index+1]
if val < minVal:
minSeam = "H"
minIndex = index
minVal = val
if minSeam == "W":
seam = [(minIndex+1,height-1)]
val = xmin[minIndex+1,height-1] - energy[minIndex+1,height-1]
cx = minIndex+1
for y in xrange(height-1):
cy = height-2-y
for d in xrange(3):
nx = cx+d-1
if xmin[nx,cy] == val:
cx = nx
seam.append((cx,cy))
val -= energy[cx,cy]
break
if d == 2:
print "Error while computing VERTICAL seam"
if targetWidth < width:
width -= 1
for x,y in seam:
for i in xrange(width-x): workingPix[x+i,y] = workingPix[x+i+1,y]
else:
width += 1
for x,y in seam:
for i in xrange(width-x-2): workingPix[width-1-i,y] = workingPix[width-2-i,y]
workingPix[x+1,y] = ((workingPix[x,y][0]+workingPix[x+1,y][0])/2,(workingPix[x,y][1]+workingPix[x+1,y][1])/2,(workingPix[x,y][2]+workingPix[x+1,y][2])/2)
elif minSeam == "H":
seam = [(width-1,minIndex+1)]
val = ymin[width-1,minIndex+1] - energy[width-1,minIndex+1]
cy = minIndex+1
for x in xrange(width-1):
cx = width-2-x
for d in xrange(3):
ny = cy+d-1
if ymin[cx,ny] == val:
cy = ny
seam.append((cx,cy))
val -= energy[cx,cy]
break
if d == 2:
print "Error while computing HORIZONTAL seam"
if targetHeight < height:
height -= 1
for x,y in seam:
for i in xrange(height-y): workingPix[x,y+i] = workingPix[x,y+i+1]
else:
height += 1
for x,y in seam:
for i in xrange(height-x-2): workingPix[x,height-1-i] = workingPix[x,height-2-i]
workingPix[x,y+1] = ((workingPix[x,y][0]+workingPix[x,y+1][0])/2,(workingPix[x,y][1]+workingPix[x,y+1][1])/2,(workingPix[x,y][2]+workingPix[x,y+1][2])/2)
else:
print "ERROR: No minimum seam found"
return working.crop((0,0,targetWidth,targetHeight))