【Python数据可视化(六)】用图像和地图绘制图表

用PIL做图像处理

  PIL坐标系统假定坐标(0,0)位于左上角。Image模块有一个非常有用的类和一些实例方法来对加载的图像对象(im)执行基本的操作。

im=Image.open(filename)打开一个文件,并把图像加载到im对象上
im.crop(box)裁剪box.box定义在左、上、右、下像素坐标(例如box=(0,100,100,100))指定的坐标区域内的图像
im.filter(filter)为图像应用一个滤波器,并返回滤波后的图像
im.histogram()返回该图像的直方图列表,其中的每一个元素代表像素值。对于单通道图像,列表中的元素数目为256,但是如果图像不是单通道图像,列表中会包含更多的元素。对于RGB图像,列表包含768个元素(每个通道有256个值)
im.resize(size,filter)重新调整图像大小,并且使用一个滤波器进行重新采样(resampling)。可能的滤波器有NEAREST,BILINEAR,BICUBIC和ANTIALIAS。默认值为NEAREST。
im.rotate(angle,filter)逆时针方向旋转图像
im.split()分离图像波段(band)并返回一个单一波段的元组。这对于分离一个RGB图像为3个单独的波段图像非常有用
im.transform(size,method,data,filter)用data和filter对一个给定的图像做转换,转换类型可以是AFFINE、EXTENT、QUAD、和MESH
  ImageDraw模块允许我们在图像上绘图,可以用arc、ellipse、pieslise、point和polygon等函数来修改所加载图像的内容。   ImageChops模块包含一些图像通道操作函数,这些函数可以被用于图像合成、着色、特效以及其他处理操作。通道操作仅限于8比特的图像。
ImageChops.duplicate(image)拷贝当前图像到一个新的图像对象
ImageChops.invert(image)反转一幅图像并返回一个副本
ImageChops.difference(image1,image2)在不用目测的情况下验证两幅图是否相同时非常有用
  ImageFilter模块包含了卷积核(convolution kernels)类的实现,这些类允许我们创建定制化的卷积核。模块还包含了一些功能健全的常用滤波器,能在图像上应用这些滤波器(BLUR和MedianFilter)。ImageFilter模块提供了两种过滤器:固定的图像增强滤波器和需要指定参数的图像滤波器,例如,把要使用的核大小作为参数。

绘制带图像的图表

import matplotlib.pyplot as plt
import matplotlib.image as im
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[0], header[1]
    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 x in range(len(years)):
        xy = int(years[x]), float(temperature[x])
        print(xy)
        ax.plot(xy[0], xy[1],'ok')
        pirate = im.imread('ship.png',0)
        # 随大小移动图像
        zoomc = int(pirates[x]) * (1 / 90000.)
        imagebox = OffsetImage(pirate, zoom=zoomc)
        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)
        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(1700, 2100)
    plt.ylim(14, 16)
    plt.title(title)
    plt.show()

在这里插入图片描述

  补充说明:
  AnnotationBbox是一个像注解一样的类,但是它能显示其他的OffsetBox实例,而不是像Axes.annotate函数那样只显示文本,这允许我们在注解中加载一幅图或者文本对象,并把它放置在与数据点有一定距离的地方,也可以使用箭头功能(arrowprops)精确地指向一个被注解的数据点。

> > > > > >
Imagebox必须是一个OffsetBox实例,它是注解框的内容
xy与注解关联的数据点坐标
xybox指定注解框的位置
xycoords指定xy使用的坐标系统
boxcoords指定xybox使用的坐标系统
pad指定内边距(padding)的数量
arrowprops用于绘制注解边框与数据点的连接箭头的属性字典

在具有其他图形的图表中显示图像

  使用Python matplotlib库来处理图像通道,并显示外部图像的单通道直方图,显示红绿蓝三个通道的图像直方图。

import matplotlib.pyplot as plt
import matplotlib.image as mpliamge
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 = mpliamge.imread(imfile)
    # 从图像矩阵中分离出RGB通道
    @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 = 'algorithm.jpg'
    try:
        iv = ImageViewer(im)
        iv.show()
    except Exception as ex:
        print(ex)

在这里插入图片描述

使用Basemap在地图上绘制数据

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

from mpl_toolkits.basemap import Basemap
import numpy as np
import matplotlib.pyplot as plt

# 墨卡托(mercator)投影,实例化Basemap,并指定左下角和右上角的经纬度
map = Basemap(projection='merc', resolution='h', area_thresh=0.1,
              # USA
              # llcrnrlon=-126.619875, llcrnrlat=31.354158,
              # urcrnrlon=-59.647219, urcrnrlat=47.517613
              # CN
              llcrnrlon=69.67111, llcrnrlat=0.875111,
              urcrnrlon=136.342914, urcrnrlat=55.23032
              )
# 绘制海岸线和国家
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()

在这里插入图片描述
  将cities.shp和cities.shx文件加载美国城市的坐标并把它们投影到地图上。

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

在这里插入图片描述

使用Google Map API在地图上绘制数据

import csv
import gviz_api


def get_page_template():
    """
    实现一个模板生成器
    :return: 
    """
    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():
    # 从本地CSV文件加载数据
    afile = "median-dpi-countries.csv"
    datarows = []
    with open(afile, 'r') as f:
        reader = csv.reader(f)
        header = next(reader)  # skip header
        for row in reader:
            datarows.append(row)

    # 描述数据
    description = {"country": ("string", "Country"),
                   "dpi": ("number", "EUR"), }

    # 使用LoadData从Python字典中加载数据
    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", )

    # 把输出渲染到Web页面
    with open('output.html', 'w') as out:
        out.write(get_page_template() % (json,))


if __name__ == '__main__':
    main()

在这里插入图片描述

  补充说明:
  使用用于Python的Google数据可视化库来为前台界面准备数据,并使用另一个Google可视化API在要求的可视化平台,也就是在地图和表格中渲染数据。

生成CAPTCHA图像

from PIL import Image, ImageDraw, ImageFont
import random
import string


class SimpleCaptchaException(Exception):
    pass


class SimpleCaptcha(object):
    # 设置图像大小、文本、字体大小、背景颜色和CAPTCHA长度
    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)

        draw = self._add_noise_lines(draw)

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

在这里插入图片描述

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZoomToday

给作者倒一杯卡布奇诺

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值