Python 数据可视化 -- pillow 处理图像

Python 图像库(Python Image Library,PIL)为 Python 提供了图像处理能力。

PIL 官网:http://www.pythonware.com/products/pil/

PIL 在线手册:http://www.pythonware.com/library/pil/handbook/index.htm

pillow 是 PIL 的一个派生分支,更加活跃。

pillow  的 github 主页:https://github.com/python-pillow/Pillow

pillow 的文档:https://pillow.readthedocs.io/en/latest/handbook/index.html

安装 pillow

pip install pillow

使用 pillow

from PIL import Image  
im = Image.open("hopper.ppm")

PIL 坐标系统假定坐标(0,0)位于左上角。

一个对图片进行过滤的例子

import os
from PIL import Image, ImageFilter

class DemoPIL(object):
    def __init__(self, image_file=None):  #  构造器
        self.fixed_filters = [ff for ff in dir(ImageFilter) if ff.isupper()]  # dir(ImageFilter)返回ImageFilter类的属性。 isupper()是str的方法,当所有字母大写返回True
        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)  # 返回一个 Image 对象实例

    def _make_temp_dir(self):
        from tempfile import mkdtemp
        self.ff_tempdir = mkdtemp(prefix="ff_demo_", dir="C:\\Users\\mail.simcere.com\\Desktop")  # 创建临时路径

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

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

if __name__ == '__main__':
#     assert len(sys.argv) == 2
    demo_image = "sunset.jpg"
    demo = DemoPIL(demo_image)  # 获取 DemoPIL 实例对象
    demo.run_fixed_filters_demo()

处理某一特定文件夹下的所有图像文件

指定一个目标路径,用程序读取目标路径下的所有图像,并按给定比例调整他们的大小,然后把每一个文件存储到另一个指令文件夹下。

import os
import sys
from PIL import Image

class Thumbnailer(object):

    def __init__(self, src_folder=None):
        self.src_folder = src_folder
        self.ratio = 0.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))
        suffix = ".thumbnail"
        return os.path.join(root, self.thumbnail_folder, name + suffix + 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)  # 返回 Image对象实例
                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__":
#     assert len(sys.argv) == 2
    src_folder = "images"  # 这里提供相对路径
    if not os.path.isdir(src_folder):
        print("Error: Path '{0}' does not exists.".format((src_folder)))
        sys.exit(-1)
    thumbs = Thumbnailer(src_folder)  # 得到 Thumbnailer的实例对象
    thumbs.thumbnail_folder = "THUMBS"  # 直接修改了对象的属性,破坏了数据封装的原则
    thumbs.ratio =0.1
    thumbs.create_thumbnails()

 

 

绘制带图像的图表

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 = next(reader)
        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)
    header, datarows = load_data()
    xlabel, ylabel, _ = header  # 将列表中的元素赋值到三个变量中
    years, temperature, pirates = format_data(datarows)
    title = 'Golbal Average Temperature vs. Number of Pirates'
    plt.plot([int(i) for i in years], [float(i) for i in temperature], lw=2)
    plt.xlabel(xlabel)
    plt.ylabel(ylabel)
    for x in range(len(years)):
        xy = int(years[x]), float(temperature[x])
        ax.plot(int(xy[0]), float(xy[1]), "ok")
        pirate = read_png('tall-ship.png')
        zoomc = int(pirates[x])*(1/90000.0)
        imagebox = OffsetImage(pirate, zoom=zoomc)  # 
        ab = AnnotationBbox(imagebox, xy, xybox=(-200.0*zoomc, 200.0*zoomc), xycoords='data', boxcoords="offset points", pad=0.1, arrowprops=dict(arrowstyle="->", connectionstyle="angle, angleA=0, angleB=-30, rad=3"))
        ax.add_artist(ab)
        no_pirates = TextArea(pirates[x], minimumdescent=False)
        ab = AnnotationBbox(no_pirates, xy, xybox=(50.0, -25.0), 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()
    plt.xlim(1800, 2020)
    plt.ylim(14, 16.5)
    plt.title(title)
    plt.show()

pirates_temperature.csv 中的内容

在有其它图形的图表中显示图像

操作步骤:

1. 加载图像

2. 从图像矩阵中分离出RGB通道

3. 配置图表和坐标轴(自区)

4. 绘制通道直方图

5. 绘制图像

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 _load_image(self, imfile):
        self.im = mplimage.imread(imfile)  # 读取图像 read an image from a file into an array
    def _configure(self):
        mpl.rcParams['font.size'] = 10
        mpl.rcParams['figure.autolayout'] = False
        mpl.rcParams['figure.figsize'] = (9, 6)
        mpl.rcParams['figure.subplot.top'] = 0.9
    @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=0.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')
        axg = self.show_channel('B')
        plt.show()
        
if __name__ == '__main__':
    im = 'yellow_flowers.jpg'
    try:
        iv = ImageViewer(im)
        iv.show()
    except Exception as ex:
        print(ex)

使用 Basemap 在地图上绘制数据

Basemap 本身不进行任何绘图工作,它只是把给定的地理坐标转换到地图投影,并把数据传给 matplotlib 进行绘图。

basemap文档:https://matplotlib.org/basemap/api/basemap_api.html

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.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)
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))
map.scatter(x, y, 25, marker='o', zorder=10)
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()

 生成验证码图像

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
        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.0, (self.size[1] - height)/2.0
        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 _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)
    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)
        draw = self._add_noise_lines(draw)
        self.image.show()
        return self.image, self.text
    
if __name__ == "__main__":
    sc = SimpleCaptcha(length=7, fontsize=36, random_text=True, random_bgcolor=True)
    sc.get_captcha()

 

posted on 2019-02-22 09:19  0820LL  阅读(2251)  评论(0编辑  收藏  举报

导航