#==============================================================================
# 第6章 用图像和地图绘制图表 145
# 6.1 简介 145
#==============================================================================
#==============================================================================
# 6.2 用PIL做图像处理 146
#==============================================================================
6.2.1 准备工作 146
6.2.2 操作步骤 149
6.2.3 工作原理 151
6.2.4 补充说明 151
import os
import sys
from PIL import Image, ImageChops, ImageFilter
'''
Image 模块
im = Image.open(filename)
im.crop(box)
im.filter(filter)
im.histogram()
im.resize(size,filter)
im.rotate(angle,filter)
im.split()
im.transform(size,method,data,filter)
ImageDraw模块
ImageChops模块
ImageFilter模块
'''
class DemoPIL(object):
def __init__(self, image_file=None):
self.fixed_filters = [ff for ff in dir(ImageFilter) if ff.isupper()]
assert image_file is not None
assert os.path.isfile(image_file) is True
self.image_file = image_file
self.image = Image.open(self.image_file)
def run_fixed_filters_demo(self):
self._make_temp_dir()
for ffilter in self.fixed_filters:
temp_img = self.apply_filter(ffilter)
temp_img.save(self._get_temp_name(ffilter))
print "Images are in: {0}".format((self.ff_tempdir),)
def _make_temp_dir(self):
from tempfile import mkdtemp
self.ff_tempdir = mkdtemp(prefix="ff_demo")
def _get_temp_name(self, filter_name):
name, ext = os.path.splitext(os.path.basename(self.image_file))
newimage_file = name + "-" + filter_name + ext
path = os.path.join(self.ff_tempdir, newimage_file)
return path
def _get_filter(self, filter_name):
real_filter = eval("ImageFilter." + filter_name)
return real_filter
def apply_filter(self, filter_name):
print "Applying filter: " + filter_name
filter_callable = self._get_filter(filter_name)
# prevent calling non-fixed filters for now
if filter_name in self.fixed_filters:
temp_img = self.image.filter(filter_callable)
else:
print "Can't apply non-fixed filter now."
return temp_img
if __name__ == "__main__":
assert len(sys.argv) == 2
demo_image = "images/sunset.jpg"
demo = DemoPIL(demo_image)
# will create set of images in temporary folder
demo.run_fixed_filters_demo()
------------------------------------------------------------------------
import os
import sys
from math import floor
from PIL import Image
class Thumbnailer(object):
def __init__(self, src_folder=None):
self.src_folder = src_folder
self.ratio = .3
self.thumbnail_folder = "thumbnails"
def _create_thumbnails_folder(self):
thumb_path = os.path.join(self.src_folder, self.thumbnail_folder)
if not os.path.isdir(thumb_path): os.makedirs(thumb_path)
def _build_thumb_path(self, image_path):
root = os.path.dirname(image_path)
name, ext = os.path.splitext(os.path.basename(image_path))
return os.path.join(root, self.thumbnail_folder, name + ".thumbnail" + ext)
def _load_files(self):
files = set()
for each in os.listdir(self.src_folder):
each = os.path.abspath(self.src_folder + '/' + each)
if os.path.isfile(each): files.add(each)
return files
def _thumb_size(self, size):
return (int(size[0] * self.ratio), int(size[1] * self.ratio))
def create_thumbnails(self):
self._create_thumbnails_folder()
files = self._load_files()
for each in files:
print "Processing: " + each
try:
img = Image.open(each)
thumb_size = self._thumb_size(img.size)
resized = img.resize(thumb_size, Image.ANTIALIAS)
savepath = self._build_thumb_path(each)
resized.save(savepath)
except IOError as ex:
print "Error: " + str(ex)
if __name__ == "__main__":
src_folder = "images"
thumbs = Thumbnailer(src_folder)
# optionally set the name of thumbnail folder inside *src_folder*.
thumbs.thumbnail_folder = "THUMBS"
# define ratio to resize image to
# 0.1 means the original image will be resized to 10% of its size
thumbs.ratio = 0.1
# will create set of images in temporary folder
thumbs.create_thumbnails()
#==============================================================================
# 6.3 绘制带图像的图表 151
#==============================================================================
6.3.1 准备工作 152
6.3.2 操作步骤 152
6.3.3 工作原理 154
import matplotlib.pyplot as plt
from matplotlib._png import read_png
from matplotlib.offsetbox import TextArea, OffsetImage, AnnotationBbox
def load_data():
import csv
with open('pirates_temperature.csv', 'r') as f:
reader = csv.reader(f)
header = reader.next()
datarows = []
for row in reader: datarows.append(row)
return header, datarows
def format_data(datarows):
years, temps, pirates = [], [], []
for each in datarows:
years.append(each[0])
temps.append(each[1])
pirates.append(each[2])
return years, temps, pirates
if __name__ == "__main__":
fig = plt.figure(figsize=(16,8))
ax = plt.subplot(111) # add sub-plot
header, datarows = load_data()
xlabel, ylabel, _ = header
years, temperature, pirates = format_data(datarows)
title = "Global Average Temperature vs. Number of Pirates"
plt.plot(years, temperature, lw=2)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
# for every data point annotate with image and number
for x in xrange(len(years)):
xy = years[x], temperature[x] # current data coordinate
ax.plot(xy[0], xy[1], "ok") # add image
pirate = read_png('tall-ship.png') # load pirate image
zoomc = int(pirates[x]) * (1 / 90000.) # zoom coefficient (move image with size)
imagebox = OffsetImage(pirate, zoom=zoomc)# create OffsetImage
'''
ImageBox:必须是一个OffsetBox实例
xy:与注解关联的数据点坐标
xybox:指定注解框的位置
xycoords:指定xy使用的坐标系统
boxcoords:指定xybox使用的坐标系统
pad:指定内边距
arrowprops:绘制注解边框与数据点的连接箭头的属性字典
'''
ab = AnnotationBbox(imagebox, xy,
xybox=(-200.*zoomc, 200.*zoomc),
xycoords='data',
boxcoords="offset points",
pad=0.1,
arrowprops=dict(arrowstyle="->", connectionstyle="angle,angleA=0,angleB=-30,rad=3")
)
ax.add_artist(ab)
# add text
no_pirates = TextArea(pirates[x], minimumdescent=False)
ab = AnnotationBbox(no_pirates, xy,
xybox=(50., -25.),
xycoords='data',
boxcoords="offset points",
pad=0.3,
arrowprops=dict(arrowstyle="->",
connectionstyle="angle,angleA=0,angleB=-30,rad=3")
)
ax.add_artist(ab)
plt.grid(1)
plt.xlim(1800, 2020)
plt.ylim(14, 16)
plt.title(title)
plt.show()
#==============================================================================
# 6.4 在具有其他图形的图表中显示图像 156
#==============================================================================
6.4.1 准备工作 156
6.4.2 操作步骤 156
6.4.3 工作原理 158
6.4.4 补充说明 159
import matplotlib.pyplot as plt
import matplotlib.image as mplimage
import matplotlib as mpl
import os
class ImageViewer(object):
def __init__(self, imfile):
self._load_image(imfile)
self._configure()
self.figure = plt.gcf()
t = "Image: {0}".format(os.path.basename(imfile))
self.figure.suptitle(t, fontsize=20)
self.shape = (3, 2)
def _configure(self):
mpl.rcParams['font.size'] = 10
mpl.rcParams['figure.autolayout'] = False
mpl.rcParams['figure.figsize'] = (9, 6)
mpl.rcParams['figure.subplot.top'] = .9
def _load_image(self, imfile):
self.im = mplimage.imread(imfile)
@staticmethod
def _get_chno(ch):
chmap = {'R': 0, 'G': 1, 'B': 2}
return chmap.get(ch, -1)
def show_channel(self, ch):
bins = 256
ec = 'none'
chno = self._get_chno(ch)
loc = (chno, 1)
ax = plt.subplot2grid(self.shape, loc)
ax.hist(self.im[:, :, chno].flatten(), bins, color=ch, ec=ec,\
label=ch, alpha=.7)
ax.set_xlim(0, 255)
plt.setp(ax.get_xticklabels(), visible=True)
plt.setp(ax.get_yticklabels(), visible=False)
plt.setp(ax.get_xticklines(), visible=True)
plt.setp(ax.get_yticklines(), visible=False)
plt.legend()
plt.grid(True, axis='y')
return ax
def show(self):
loc = (0, 0)
axim = plt.subplot2grid(self.shape, loc, rowspan=3)
axim.imshow(self.im)
plt.setp(axim.get_xticklabels(), visible=False)
plt.setp(axim.get_yticklabels(), visible=False)
plt.setp(axim.get_xticklines(), visible=False)
plt.setp(axim.get_yticklines(), visible=False)
axr = self.show_channel('R')
axg = self.show_channel('G')
axb = self.show_channel('B')
plt.show()
if __name__ == '__main__':
im = 'images/yellow_flowers.jpg'
try:
iv = ImageViewer(im)
iv.show()
except Exception as ex:
print ex
#==============================================================================
# 6.5 使用Basemap在地图上绘制数据
#==============================================================================
http://matplotlib.org/basemap/users/examples.html
Windows
https://sourceforge.net/projects/matplotlib/files/matplotlib-toolkits/basemap-1.0.2/
注册表中注册Python
------------------------------
import sys
from _winreg import *
# tweak as necessary
version = sys.version[:3]
installpath = sys.prefix
regpath = "SOFTWARE\\Python\\Pythoncore\\%s\\" % (version)
installkey = "InstallPath"
pythonkey = "PythonPath"
pythonpath = "%s;%s\\Lib\\;%s\\DLLs\\" % (
installpath, installpath, installpath
)
def RegisterPy():
print "begin RegisterPy "
try:
print "open key : %s"%regpath
reg = OpenKey(HKEY_CURRENT_USER, regpath)
except EnvironmentError as e:
try:
reg = CreateKey(HKEY_CURRENT_USER, regpath)
SetValue(reg, installkey, REG_SZ, installpath)
SetValue(reg, pythonkey, REG_SZ, pythonpath)
CloseKey(reg)
except:
print "*** EXCEPT: Unable to register!"
return
print "--- Python", version, "is now registered!"
return
if (QueryValue(reg, installkey) == installpath and
QueryValue(reg, pythonkey) == pythonpath):
CloseKey(reg)
print "=== Python", version, "is already registered!"
return CloseKey(reg)
print "*** ERROR:Unable to register!"
print "*** REASON:You probably have another Python installation!"
def UnRegisterPy():
#print "begin UnRegisterPy "
try:
print "open HKEY_CURRENT_USER key=%s"%(regpath)
reg = OpenKey(HKEY_CURRENT_USER, regpath)
#reg = OpenKey(HKEY_LOCAL_MACHINE, regpath)
except EnvironmentError:
print "*** Python not registered?!"
return
try:
DeleteKey(reg, installkey)
DeleteKey(reg, pythonkey)
DeleteKey(HKEY_LOCAL_MACHINE, regpath)
except:
print "*** Unable to un-register!"
else:
print "--- Python", version, "is no longer registered!"
if __name__ == "__main__":
RegisterPy()
---------------------------------------------------------------------------------------
Linux
https://github.com/matplotlib/basemap
进入 GEOS 子目录
./configure --enable-python
make
sudo make install
回到顶级目录
sudo python setup.py install
----------------------------------------------------
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
map = Basemap(projection='merc', resolution = 'h', area_thresh = 0.1,
llcrnrlon=-126.619875, llcrnrlat=31.354158,
urcrnrlon=-59.647219, urcrnrlat=47.517613)
map.drawcoastlines()
map.drawcountries()
map.fillcontinents(color='coral', lake_color='aqua')
map.drawmapboundary(fill_color='aqua')
map.drawmeridians(np.arange(0, 360, 30))
map.drawparallels(np.arange(-90, 90, 30))
plt.title('Cities in USA')
plt.show()
----------------------------------------------------------------
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
map = Basemap(projection='merc',
resolution = 'h',
area_thresh = 100,
llcrnrlon=-126.619875, llcrnrlat=25,
urcrnrlon=-59.647219, urcrnrlat=55)
shapeinfo = map.readshapefile('cities','cities')
x, y = zip(*map.cities)
# build a list of US cities
city_names = []
for each in map.cities_info:
if each['COUNTRY'] != 'US':
city_names.append("")
else:
city_names.append(each['NAME'])
map.drawcoastlines()
map.drawcountries()
map.fillcontinents(color='coral', lake_color='aqua')
map.drawmapboundary(fill_color='aqua')
map.drawmeridians(np.arange(0, 360, 30))
map.drawparallels(np.arange(-90, 90, 30))
# draw city markers
map.scatter(x,y,25, marker='o',zorder=10)
# plot labels at City coords.
for city_label, city_x, city_y in zip(city_names, x, y):
plt.text(city_x, city_y, city_label)
plt.title('Cities in USA')
plt.show()
#==============================================================================
# 6.6 使用Google MapAPI在地图上绘制数据
#==============================================================================
https://github.com/google/google-visualization-python
import csv
import gviz_api
def get_page_template():
page_template = """
<html>
<script src="https://www.google.com/jsapi" type="text/javascript"></script>
<script>
google.load('visualization', '1', {packages:['geochart', 'table']});
google.setOnLoadCallback(drawMap);
function drawMap() {
var json_data = new google.visualization.DataTable(%s, 0.6);
var options = {colorAxis: {colors: ['#eee', 'green']}};
var mymap = new google.visualization.GeoChart(document.getElementById('map_div'));
mymap.draw(json_data, options);
var mytable = new google.visualization.Table(document.getElementById('table_div'));
mytable.draw(json_data, {showRowNumber: true})
}
</script>
<body>
<H1>Median Monthly Disposable Salary World Countries</H1>
<div id="map_div"></div>
<hr />
<div id="table_div"></div>
<div id="source">
<hr />
<small>
Source: <a href="http://www.numbeo.com/cost-of-living/prices_by_country.jsp?displayCurrency=EUR&itemId=105">
http://www.numbeo.com/cost-of-living/prices_by_country.jsp?displayCurrency=EUR&itemId=105 </a>
</small>
</div>
</body>
</html>
"""
return page_template
def main():
# Load data from CVS file
afile = "median-dpi-countries.csv"
datarows = []
with open(afile, 'r') as f:
reader = csv.reader(f)
reader.next() # skip header
for row in reader:
datarows.append(row)
# Describe data
description = {"country": ("string", "Country"), "dpi": ("number", "EUR"), }
data = []
for each in datarows:
data.append({"country": each[0], "dpi": (float(each[1]), each[1])})
data_table = gviz_api.DataTable(description)
data_table.LoadData(data)
json = data_table.ToJSon(columns_order=("country", "dpi"), order_by="country", )
# Put JSON string into the template and save to output.html
with open('output.html', 'w') as out:
out.write(get_page_template() % (json,))
if __name__ == '__main__':
main()
#==============================================================================
# 6.7 生成CAPTCHA图像
#==============================================================================
from PIL import Image, ImageDraw, ImageFont
import random
import string
class SimpleCaptchaException(Exception):
pass
class SimpleCaptcha(object):
def __init__(self, length=5, size=(200, 100), fontsize=36,random_text=None, random_bgcolor=None):
self.size = size
self.text = "CAPTCHA"
self.fontsize = fontsize
self.bgcolor = 255
self.length = length
self.image = None # current captcha image
if random_text: self.text = self._random_text()
if not self.text: raise SimpleCaptchaException("Field text must not be empty.")
if not self.size: raise SimpleCaptchaException("Size must not be empty.")
if not self.fontsize: raise SimpleCaptchaException("Font size must be defined.")
if random_bgcolor: self.bgcolor = self._random_color()
def _center_coords(self, draw, font):
width, height = draw.textsize(self.text, font)
xy = (self.size[0] - width) / 2., (self.size[1] - height) / 2.
return xy
def _add_noise_dots(self, draw):
size = self.image.size
for _ in range(int(size[0] * size[1] * 0.1)):
draw.point((random.randint(0, size[0]), random.randint(0, size[1])), fill="white")
return draw
def _add_noise_lines(self, draw):
size = self.image.size
for _ in range(8):
width = random.randint(1, 2)
start = (0, random.randint(0, size[1] - 1))
end = (size[0], random.randint(0, size[1] - 1))
draw.line([start, end], fill="white", width=width)
for _ in range(8):
start = (-50, -50)
end = (size[0] + 10, random.randint(0, size[1] + 10))
draw.arc(start + end, 0, 360, fill="white")
return draw
def get_captcha(self, size=None, text=None, bgcolor=None):
if text is not None: self.text = text
if size is not None: self.size = size
if bgcolor is not None: self.bgcolor = bgcolor
self.image = Image.new('RGB', self.size, self.bgcolor)
font = ImageFont.truetype('fonts/Vera.ttf', self.fontsize)
draw = ImageDraw.Draw(self.image)
xy = self._center_coords(draw, font)
draw.text(xy=xy, text=self.text, font=font)
draw = self._add_noise_dots(draw) # Add some noise
draw = self._add_noise_lines(draw)# Add some random lines
self.image.show()
return self.image, self.text
def _random_text(self):
letters = string.ascii_lowercase + string.ascii_uppercase
random_text = ""
for _ in range(self.length): random_text += random.choice(letters)
return random_text
def _random_color(self):
r = random.randint(0, 255)
g = random.randint(0, 255)
b = random.randint(0, 255)
return (r, g, b)
if __name__ == "__main__":
sc = SimpleCaptcha(length=7, fontsize=36, random_text=True, random_bgcolor=True)
sc.get_captcha()