Python如何使用Pillow库处理图像?完整指南

2021年11月11日01:54:57 发表评论 646 次浏览

Pillow库处理图像指南介绍

许多应用程序使用数字图像,因此通常需要处理所使用的图像。如果你使用 Python 构建应用程序并需要为其添加图像处理功能,则可以使用各种库。一些流行的有OpenCVscikit-imagePython Imaging LibraryPillow

我们不会在这里讨论哪个库是最好的;他们都有自己的优点。本文将重点介绍 Pillow,这是一个功能强大的库,可提供广泛的图像处理功能且易于使用。

如何使用Pillow库处理图像?Pillow 是 Python Imaging Library (PIL) 的一个分支。PIL 是一个库,它提供了几个操作图像的标准程序。Pillow如何处理图像?它是一个强大的库,但自 2009 年以来一直没有更新,并且不支持 Python 3。 Pillow 以此为基础,添加了更多功能并支持 Python 3。它支持一系列图像文件格式,如 PNG、JPEG、PPM、 GIF、TIFF 和 BMP。我们将看到如何使用这个库对图像执行各种操作,包括一些Pillow库处理图像示例,例如裁剪、调整大小、向图像添加文本、旋转、灰度缩放等。

安装和项目设置

在安装 Pillow 之前,你应该了解以下几点:

  • Pillow 和 PIL 不能在同一个环境中共存,所以如果你安装了 PIL,请先卸载它再继续。
  • 我们将在本文中使用 Pillow 的当前稳定版本(在撰写本文时为 8.0.1 版)。此版本需要 Python 3.6 及以上版本。

我们在下面提供了有关如何安装 Pillow 的说明,但最好查看安装指南,以防 Pillow 的更高版本碰巧需要先安装一些先决条件库。

你可以使用pip如下所示安装 Pillow :

python3 -m pip install --upgrade pip
python3 -m pip install --upgrade Pillow

接下来,你可以下载我们将在本文中使用的图像(由Unsplash 提供)。你也可以使用自己的图像。

所有示例都假定所需的图像与正在运行的 python 脚本文件位于同一目录中。

图像对象

Pillow库处理图像指南:Python 成像库中的一个关键类是Image类。它在Image模块中定义并提供一个 PIL 图像,可以在其上执行操作操作。可以通过多种方式创建此类的实例:通过从文件加载图像、从头开始创建图像或作为处理其他图像的结果。我们将看到所有这些都在使用中。

要从文件加载图像,我们使用模块中的函数,将图像路径传递给它。open()Image

from PIL import Image

image = Image.open('demo_image.jpg')

如果成功,上面的返回一个Image对象。如果打开文件时出现问题,OSError将引发异常。

Pillow库处理图像示例:获得一个Image对象后,你现在可以使用该类定义的方法和属性来对其进行处理和操作。让我们从显示图像开始。你可以通过调用它的方法来做到这一点。这会在外部查看器上显示图像(通常在 macOS 上预览,在 Unix 上为 xv,在 Windows 上为 Paint 程序)。show()

image.show()

你可以使用对象的属性获取有关图像的一些详细信息。

# The file format of the source file.
print(image.format) # Output: JPEG

# The pixel format used by the image. Typical values are "1", "L", "RGB", or "CMYK."
print(image.mode) # Output: RGB

# Image size, in pixels. The size is given as a 2-tuple (width, height).
print(image.size) # Output: (1920, 1280)

# Colour palette table, if any.
print(image.palette) # Output: None

有关你可以使用Image该类做什么的更多信息,请查看文档

更改图像类型

处理完图像后,可以使用方法将其保存到文件中,传入将用于标记图像文件的名称。保存图像时,你可以指定与原始图像不同的扩展名,保存的图像将转换为指定的格式。save()

image = Image.open('demo_image.jpg')
image.save('new_image.png')

上面创建了一个加载了图像的 Image 对象并将其保存到一个新文件中,Pillow 看到文件扩展名已被指定为 PNG,因此它在将其保存到文件之前将其转换为 PNG。你可以提供第二个参数来显式指定文件格式。这将做与前一个相同的事情。通常,没有必要提供第二个参数,因为 Pillow 将根据文件扩展名确定要使用的文件存储格式,但如果你使用的是非标准扩展名,则应始终以这种方式指定格式。demo_image.jpgnew_image.png.save()image.save('new_image.png', 'PNG')save()

调整图像大小

Pillow如何处理图像?要调整图像大小,请调用其上的方法,传入一个代表调整大小图像的宽度和高度的两个整数元组参数。该函数不会修改使用的图像;相反,它返回另一个具有新尺寸的图像。resize()

image = Image.open('demo_image.jpg')
new_image = image.resize((400, 400))
new_image.save('image_400.jpg')

print(image.size) # Output: (1920, 1280)
print(new_image.size) # Output: (400, 400)

该方法返回一个图像,其宽度和高度与传入的值完全匹配。这可能是你想要的,但有时你可能会发现此函数返回的图像并不理想。这主要是因为该函数没有考虑图像的纵横比,因此你最终可能会得到看起来被拉伸或压扁的图像。resize()

你可以从上面的代码中新创建的图像中看到这一点:。它看起来有点水平压扁。image_400.jpg

Python如何使用Pillow库处理图像?完整指南

如果你想调整图像大小并保持它们的纵横比,那么你应该使用该函数来调整它们的大小。这也需要一个代表缩略图的最大宽度和最大高度的两个整数元组参数。thumbnail()

image = Image.open('demo_image.jpg')
image.thumbnail((400, 400))
image.save('image_thumbnail.jpg')

print(image.size) # Output: (400, 267)

Pillow库处理图像示例:以上将导致图像大小为 400x267,并保持原始图像的纵横比。正如你在下面看到的,这会产生更好看的图像。

Python如何使用Pillow库处理图像?完整指南

和函数之间的另一个显着区别是,如果给定的参数大于原始图像,函数会“炸毁”图像,而函数不会。例如,给定尺寸为 400x200 的图像,调用将创建一个更大尺寸的图像 1200x600;因此,图像会失去一些清晰度,并且与原始图像相比可能会模糊。另一方面,调用使用原始图像将导致图像保持其大小为 400x200,因为宽度和高度都小于指定的最大宽度和高度。resize()thumbnail()resize()thumbnail()resize((1200, 600))thumbnail((1200, 600))

裁剪

如何使用Pillow库处理图像?裁剪图像时,会选择并保留图像内的矩形区域,同时移除该区域外的所有其他内容。使用 Pillow 库,你可以使用类的方法裁剪图像。该方法采用一个框元组来定义裁剪区域的位置和大小,并返回一个表示裁剪图像的对象。框的坐标为(左、上、右、下)。裁剪部分包括左列和上一行像素,并向上(但不包括)右列和下一行像素。用一个例子可以更好地解释这一点。crop()ImageImage

image = Image.open('demo_image.jpg')
box = (200, 300, 700, 600)
cropped_image = image.crop(box)
cropped_image.save('cropped_image.jpg')

# Print size of cropped image
print(cropped_image.size) # Output: (500, 300)

这是生成的图像:

Python如何使用Pillow库处理图像?完整指南

Python 图像库使用左上角以 (0, 0) 开头的坐标系。框元组的前两个值指定裁剪框的左上角起始位置。第三个和第四个值分别指定从该起始位置到右侧和底部方向的距离(以像素为单位)。坐标是指像素之间的位置,因此上例中的区域正好是 500x300 像素。

Pillow库处理图像指南:将图像粘贴到另一个图像上

Pillow 使你可以将图像粘贴到另一个图像上。这可能有用的一些示例用例是通过在公共图像上添加水印来保护它们,通过添加公司徽标来对图像进行品牌化,以及在需要合并两个图像的任何其他情况下。

粘贴是用函数完成的。这与我们目前看到的返回新对象的其他处理函数不同,这会就地修改对象。因此,在执行粘贴之前,我们将首先复制演示图像,以便我们可以使用未修改的图像继续其他示例。paste()ImageImage

image = Image.open('demo_image.jpg')
logo = Image.open('logo.png')
image_copy = image.copy()
position = ((image_copy.width - logo.width), (image_copy.height - logo.height))
image_copy.paste(logo, position)
image_copy.save('pasted_image.jpg')

在上文中,我们加载两个图像,并且,然后使前者的副本。我们希望将徽标图像粘贴到复制的图像上,并将其放置在右下角。这是计算并保存在一个元组中。元组可以是给出左上角的 2 元组,也可以是定义左、上、右和下像素坐标的 4 元组,或者(与 (0, 0) 相同)。然后我们将这个元组与将被粘贴的图像一起传递。unsplash_01.jpglogo.pngcopy()Nonepaste()

你可以在下面看到结果。

Python如何使用Pillow库处理图像?完整指南

那不是我们期望的结果。

默认情况下,当你执行粘贴时,透明像素将粘贴为实心像素,因此徽标周围的黑色(在某些操作系统上为白色)框。大多数时候,这不是你想要的。你不能让水印覆盖底层图像的内容。我们宁愿让透明像素这样出现。

为此,你需要向函数传递第三个参数。此参数是透明蒙版 Image 对象。蒙版是一个 Image 对象,其中 alpha 值很重要,但它的绿色、红色和蓝色值被忽略。如果给出了掩码,则仅更新掩码指示的区域。你可以使用,或为掩盖图像。粘贴 RGBA 图像并将其用作蒙版将粘贴图像的不透明部分,但不会粘贴其透明背景。如果你按如下所示修改粘贴,你应该有一个带有透明像素的粘贴徽标。paste()paste()1LRGBA

image_copy.paste(logo, position, logo)
Python如何使用Pillow库处理图像?完整指南

Pillow如何处理图像?旋转图像

你可以使用方法使用 Pillow 旋转图像。这需要一个整数或浮点参数表示旋转图像的度数,并返回旋转图像的新 Image 对象。旋转是逆时针完成的。rotate()

image = Image.open('demo_image.jpg')

image_rot_90 = image.rotate(90)
image_rot_90.save('image_rot_90.jpg')

image_rot_180 = image.rotate(180)
image_rot_180.save('image_rot_180.jpg')

在上面,我们将两张图像保存到磁盘:一张旋转了 90 度,另一张旋转了 180 度。生成的图像如下所示。

Python如何使用Pillow库处理图像?完整指南
Python如何使用Pillow库处理图像?完整指南

默认情况下,旋转后的图像保持原始图像的尺寸。这意味着对于 180 倍数以外的角度,图像将被剪切和/或填充以适应原始尺寸。如果你仔细查看上面的第一张图片,你会注意到其中一些已被裁剪以适应原始高度,并且其两侧已填充黑色背景(某些操作系统上的透明像素)以适应原始宽度。下面的例子更清楚地说明了这一点。

image.rotate(18).save('image_rot_18.jpg')

结果图像如下所示:

Python如何使用Pillow库处理图像?完整指南

要扩展旋转图像的尺寸以适合整个视图,请传递第二个参数 ,如下所示。rotate()

image.rotate(18, expand=True).save('image_rot_18.jpg')

现在图像的内容将完全可见,并且图像的尺寸将增加以解决此问题。

Python如何使用Pillow库处理图像?完整指南

翻转图像

如何使用Pillow库处理图像?你还可以翻转图像以获得它们的镜像版本。这是通过函数完成的。它采用下列选项之一:,,,,或。transpose()PIL.Image.FLIP_LEFT_RIGHTPIL.Image.FLIP_TOP_BOTTOMPIL.Image.ROTATE_90PIL.Image.ROTATE_180PIL.Image.ROTATE_270 PIL.Image.TRANSPOSEPIL.Image.TRANSVERSE

image = Image.open('demo_image.jpg')

image_flip = image.transpose(Image.FLIP_LEFT_RIGHT)
image_flip.save('image_flip.jpg')

生成的图像如下所示。

Python如何使用Pillow库处理图像?完整指南

在图像上绘图

Pillow如何处理图像?使用 Pillow,你还可以使用ImageDraw模块在图像上绘制。你可以绘制线条、点、椭圆、矩形、圆弧、位图、弦、饼图、多边形、形状和文本。

from PIL import Image, ImageDraw

canvas = Image.new('RGB', (400, 300), 'white')
img_draw = ImageDraw.Draw(canvas)
img_draw.rectangle((70, 50, 270, 200), outline='red', fill='blue')
img_draw.text((70, 250), 'Hello World', fill='green')
canvas.save('drawn_image.jpg')

在示例中,我们使用该方法创建了一个 Image 对象。这将返回一个没有加载图像的对象。然后我们在保存图像之前向图像添加一个矩形和一些文本。new()Image

Python如何使用Pillow库处理图像?完整指南

颜色变换

Pillow库处理图像示例:模式间转换

Pillow 库使你能够使用该方法在不同像素表示之间转换图像。它支持(灰度)、 和模式之间的转换。convert()LRGBCMYK

在下面的例子中,我们从将图像转换RGBL(亮度)模式,这将导致在灰度图像。

image = Image.open('demo_image.jpg')

greyscale_image = image.convert('L')
greyscale_image.save('greyscale_image.jpg')

print(image.mode) # Output: RGB
print(greyscale_image.mode) # Output: L
Python如何使用Pillow库处理图像?完整指南

拆分和合并频段

你还可以使用该方法将多波段图像(例如 RGB)拆分为单独的波段。创建新图像,每个图像包含来自原始图像的一个波段。split()split()

你可以使用该功能将一组单波段图像合并为一个新的多波段图像。获取一个模式和一个图像元组,并将它们组合成一个新图像。merge()merge()

image = Image.open('demo_image.jpg')

red, green, blue = image.split()

print(image.mode) # Output: RGB
print(red.mode) # Output: L
print(green.mode) # Output: L
print(blue.mode) # Output: L

new_image = Image.merge("RGB", (green, red, blue))
new_image.save('new_image.jpg')

print(new_image.mode) # Output: RGB

在上面的代码中,我们将 RGB 图像拆分为单独的波段,交换它们,然后合并它们。下面是生成的图像。

Python如何使用Pillow库处理图像?完整指南

Pillow库处理图像指南:图像增强

Pillow 允许你通过使用ImageEnhance模块中的类调整对比度、颜色、亮度和锐度来增强图像。

from PIL import Image, ImageEnhance

image = Image.open('demo_image.jpg')

contrast = ImageEnhance.Contrast(image)
contrast.enhance(1.5).save('contrast.jpg')

在上面,我们通过因子调整图像对比度1.5。增强类中使用的因子是一个浮点值,它决定了增强的级别。一个因子1.0返回原始图像的副本;较低的因素意味着较少的特定增强和较高的值更多。该值没有限制。

你可以在下面看到增强的图像。

Python如何使用Pillow库处理图像?完整指南

下面,我们增加图像的颜色。如果我们使用 的因子0.0,我们将得到一个黑白图像。

color = ImageEnhance.Color(image)
color.enhance(1.5).save('color.jpg')
Python如何使用Pillow库处理图像?完整指南

下面我们使图像更亮。一个因素0.0会产生一个黑色的图像。

brightness = ImageEnhance.Brightness(image)
brightness.enhance(1.5).save('brightness.jpg')
Python如何使用Pillow库处理图像?完整指南

下面我们使图像更清晰。增强因子0.0会产生模糊图像,而因子2.0会产生锐化图像。

sharpness = ImageEnhance.Sharpness(image)
sharpness.enhance(1.5).save('sharpness.jpg')
Python如何使用Pillow库处理图像?完整指南

旁白:向 Python 应用程序添加 Auth0 身份验证

在结束本文之前,让我们看一下如何使用 Auth0 向 Python 应用程序添加身份验证。我们将看到的应用程序是用 Flask 制作的,但其他 Python Web 框架的过程类似。

我没有从头开始创建应用程序,而是将一个简单的应用程序放在一起,你可以下载它以进行后续操作。它是一个简单的图库应用程序,使用户能够将图像上传到服务器并查看上传的图像。

如果你下载了项目文件,你会在主目录中找到两个文件夹:complete_without_auth0complete_with_auth0. 顾名思义,complete_without_auth0就是我们将开始并添加 Auth0 的项目。

要运行代码,最好创建一个虚拟环境并在那里安装所需的包。这可以防止系统的全局 Python 解释器中的包混乱和版本冲突。

Pillow如何处理图像?我们将介绍使用 Python 3 创建虚拟环境。此版本本身支持虚拟环境,不需要下载外部实用程序 (virtualenv),就像 Python 2.7 一样。

下载代码文件后,将你的终端更改为指向该文件夹。completed_without_auth0/gallery_demo

$ cd path/to/complete_without_auth0/gallery_demo

使用以下命令创建虚拟环境。

$ python3 -m venv venv

然后使用(在 macOS 和 Linux 上)激活它:

$ source venv/bin/activate

在 Windows 上:

$ venv\Scripts\activate

要完成设置,请使用以下命令安装 requirements.txt 文件中列出的软件包:

$ pip install -r requirements.txt

Pillow库处理图像示例:这将安装flask、、、、、包及其依赖项。当问题是安装,你可能在你的终端阅读收到一条错误消息。从我看到的情况来看,必要的包将被安装,并且无需执行任何操作即可完成设置(你应该在终端中看到消息)。将成功安装,演示项目将正常运行。你可以在此处阅读有关错误消息的更多信息flask-bootstrappython-dotenvpillowauthlibrequestsflask-bootstrapERROR: Failed building wheel for visitorRunning setup.py install for visitor ... doneflask-bootstrap

最后,运行应用程序。

$ Python app.py

在浏览器中打开http://localhost:3000/,你应该会看到以下页面。

Python如何使用Pillow库处理图像?完整指南

当你转到http://localhost:3000/gallery 时,你会看到一个空白页面。你可以前往http://localhost:3000/upload并上传一些图片,然后这些图片将出现在图库中。

Python如何使用Pillow库处理图像?完整指南
Python如何使用Pillow库处理图像?完整指南

如何使用Pillow库处理图像?上传图像时,会使用我们之前看到的功能制作一个较小的副本,然后将这两个图像保存 - 原件保存在文件夹中,另一个保存在缩略图文件夹中。thumbnail()imagesthumbnail

图库显示较小尺寸的缩略图,并且仅在单击缩略图时显示较大的图像(在模态内)。

就该应用程序而言,任何用户都可以上传图像。这可能并不理想。最好对此操作进行一些保护以防止滥用或至少跟踪用户上传。这就是 Auth0 的用武之地。使用 Auth0,我们将能够以最少的工作量向应用程序添加身份验证。

为了应用程序的简单性,它的大部分功能都在文件中。在这里,你可以看到设置的路由处理程序。该函数处理对 的调用。这是在保存图像之前处理图像的地方。我们将使用 Auth0 保护这条路线。app.pyupload()/upload

from flask import Flask, render_template, redirect, url_for, send_from_directory, request
from flask_bootstrap import Bootstrap
from PIL import Image
from werkzeug.utils import secure_filename
import os

app = Flask(__name__)
Bootstrap(app)

APP_ROOT = os.path.dirname(os.path.abspath(__file__))
images_directory = os.path.join(APP_ROOT, 'images')
thumbnails_directory = os.path.join(APP_ROOT, 'thumbnails')
if not os.path.isdir(images_directory):
    os.mkdir(images_directory)
if not os.path.isdir(thumbnails_directory):
    os.mkdir(thumbnails_directory)


@app.route('/')
def index():
    return render_template('index.html')


@app.route('/gallery')
def gallery():
    thumbnail_names = os.listdir('./thumbnails')
    return render_template('gallery.html', thumbnail_names=thumbnail_names)


@app.route('/thumbnails/<filename>')
def thumbnails(filename):
    return send_from_directory('thumbnails', filename)


@app.route('/images/<filename>')
def images(filename):
    return send_from_directory('images', filename)


@app.route('/public/<path:filename>')
def static_files(filename):
    return send_from_directory('./public', filename)


@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        for upload in request.files.getlist('images'):
            filename = upload.filename
            # Always a good idea to secure a filename before storing it
            filename = secure_filename(filename)
            # This is to verify files are supported
            ext = os.path.splitext(filename)[1][1:].strip().lower()
            if ext in {'jpg', 'jpeg', 'png'}:
                print('File supported moving on...')
            else:
                return render_template('error.html', message='Uploaded files are not supported...')
            destination = '/'.join([images_directory, filename])
            # Save original image
            upload.save(destination)
            # Save a copy of the thumbnail image
            image = Image.open(destination)
            image.thumbnail((300, 170))
            image.save('/'.join([thumbnails_directory, filename]))
        return redirect(url_for('gallery'))
    return render_template('upload.html')


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=os.environ.get('PORT', 3000))

Pillow库处理图像指南:设置 Auth0

要使用 Auth0 设置应用程序,首先注册一个 Auth0 帐户,然后导航到Dashboard。单击创建应用程序按钮并填写应用程序的名称(或保留其默认值)。从应用程序类型列表中选择常规 Web 应用程序,然后创建应用程序。

Python如何使用Pillow库处理图像?完整指南

请注意:

如果你进入Getting Started屏幕,请单击Create Application按钮,该按钮位于标记为Integrate Auth0 into your application 的区域中。你将被带到你的项目使用什么技术?屏幕,在这里只需单击“跳过集成”按钮,这将带你进入应用程序的“设置”选项卡,你可以在其中访问客户端 ID、客户端密钥和域。

创建应用程序后,选择可以检索客户端 ID、客户端密钥和域的设置选项卡。将允许回调的URL来和允许退出的URL来,然后用保存在页面底部的按钮的变化。http://localhost:3000/callbackhttp://localhost:3000

回到你的项目中,创建一个标记的文件.env并将其保存在项目的根目录下。将你的 Auth0 客户端凭据添加到此文件。如果你使用版本控制,请记住不要将此文件置于版本控制之下。我们将使用 的值SECRET_KEY作为应用程序的密钥。你可以/应该改变它。

AUTH0_CLIENT_ID=YOUR_AUTH0_CLIENT_ID
AUTH0_DOMAIN=YOUR_AUTH0_DOMAIN
AUTH0_CLIENT_SECRET=YOUR_AUTH0_CLIENT_SECRET
AUTH0_CALLBACK_URL=http://localhost:3000/callback
SECRET_KEY=F12ZMr47j\3yXgR~X@H!jmM]6Lwf/,4?KT

将另一个名为的文件添加到项目的根目录,并在其中添加以下常量。constants.py

AUTH0_CLIENT_ID = 'AUTH0_CLIENT_ID'
AUTH0_CLIENT_SECRET = 'AUTH0_CLIENT_SECRET'
AUTH0_CALLBACK_URL = 'AUTH0_CALLBACK_URL'
AUTH0_DOMAIN = 'AUTH0_DOMAIN'
PROFILE_KEY = 'profile'
JWT_PAYLOAD = 'jwt_payload'

接下来,如图所示修改文件的开头— 从第一个语句到第一个路由定义 ( )之前的点。app.py@app.route('/')

from flask import Flask, render_template, redirect, url_for, send_from_directory, request, session, jsonify
from flask_bootstrap import Bootstrap
from PIL import Image
from werkzeug.utils import secure_filename
from werkzeug.exceptions import HTTPException
from dotenv import load_dotenv, find_dotenv
from functools import wraps
from authlib.integrations.flask_client import OAuth
import urllib.parse
import os
import constants

# Load Env variables
ENV_FILE = find_dotenv()
if ENV_FILE:
    load_dotenv(ENV_FILE)

app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY')
Bootstrap(app)

AUTH0_CALLBACK_URL = os.environ.get(constants.AUTH0_CALLBACK_URL)
AUTH0_CLIENT_ID = os.environ.get(constants.AUTH0_CLIENT_ID)
AUTH0_CLIENT_SECRET = os.environ.get(constants.AUTH0_CLIENT_SECRET)
AUTH0_DOMAIN = os.environ.get(constants.AUTH0_DOMAIN)
AUTH0_BASE_URL = 'https://' + AUTH0_DOMAIN

APP_ROOT = os.path.dirname(os.path.abspath(__file__))
images_directory = os.path.join(APP_ROOT, 'images')
thumbnails_directory = os.path.join(APP_ROOT, 'thumbnails')
if not os.path.isdir(images_directory):
    os.mkdir(images_directory)
if not os.path.isdir(thumbnails_directory):
    os.mkdir(thumbnails_directory)


@app.errorhandler(Exception)
def handle_auth_error(ex):
    response = jsonify(message=str(ex))
    response.status_code = (ex.code if isinstance(ex, HTTPException) else 500)
    return response


oauth = OAuth(app)

auth0 = oauth.register(
    'auth0',
    client_id=AUTH0_CLIENT_ID,
    client_secret=AUTH0_CLIENT_SECRET,
    api_base_url=AUTH0_BASE_URL,
    access_token_url=AUTH0_BASE_URL + '/oauth/token',
    authorize_url=AUTH0_BASE_URL + '/authorize',
    client_kwargs={
        'scope': 'openid profile email',
    },
)

我们用来从文件加载环境变量。load_dotenv().env

然后我们设置应用程序的secret_key. 该应用程序将利用会话,它允许从一个请求到下一个请求存储特定于用户的信息。这是在 cookie 之上实现的,并以加密方式对 cookie 进行签名。这意味着某人可以查看你的 cookie 的内容,但无法确定底层凭据或成功修改它,除非他们知道用于签名的密钥。

接下来,我们将 Auth0 凭据保存在稍后将使用的一些常量中,并添加一个错误处理程序 ( handle_auth_error)。我们在错误处理程序上使用装饰器,它配置 Flask在引发类型异常时调用此函数。错误处理程序通过将错误放置在 JSON 对象中使错误更具可读性。@app.errorhandlerException

然后我们初始化一个Flask OAuth 客户端并注册我们的应用程序。

接下来,将以下函数添加到路由处理程序定义之前的文件中。必须出现在任何路由处理程序定义之前,否则将引发错误。app.pyrequires_auth()NameError: name 'requires_auth' is not defined

# Requires authentication decorator
def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        if is_logged_in():
            return f(*args, **kwargs)
        return redirect('/')

    return decorated


def is_logged_in():
    return constants.PROFILE_KEY in session

在这里,我们定义了一个装饰器,以确保用户在访问特定路由之前已通过身份验证。第二个函数简单地返回TrueFalse取决于会话对象中是否存储了来自 Auth0 的一些用户数据。

接下来,修改和功能,如图所示。index()upload()

@app.route('/')
def index():
    return render_template('index.html', env=os.environ, logged_in=is_logged_in())


@app.route('/upload', methods=['GET', 'POST'])
@requires_auth
def upload():
    if request.method == 'POST':
        for upload in request.files.getlist('images'):
            filename = upload.filename
            # Always a good idea to secure a filename before storing it
            filename = secure_filename(filename)
            # This is to verify files are supported
            ext = os.path.splitext(filename)[1][1:].strip().lower()
            if ext in {'jpg', 'jpeg', 'png'}:
                print('File supported moving on...')
            else:
                return render_template('error.html', message='Uploaded files are not supported...')
            destination = '/'.join([images_directory, filename])
            # Save original image
            upload.save(destination)
            # Save a copy of the thumbnail image
            image = Image.open(destination)
            image.thumbnail((300, 170))
            image.save('/'.join([thumbnails_directory, filename]))
        return redirect(url_for('gallery'))
    return render_template('upload.html', user=session[constants.PROFILE_KEY])

在 中,我们将一些变量传递给模板。我们稍后会用到这些。index()index.html

我们将@requires_auth装饰器添加到函数中。这将确保只有在用户登录时调用才能成功。未经身份验证的用户不仅无法访问页面,而且他们也无法将数据发布到路由。upload()/uploadupload.html

在函数结束时,我们将一个user变量传递给模板。upload.html

接下来,将以下函数添加到文件中。

@app.route('/callback')
def callback_handling():
    auth0.authorize_access_token()
    resp = auth0.get('userinfo')
    userinfo = resp.json()

    session[constants.JWT_PAYLOAD] = userinfo
    session[constants.PROFILE_KEY] = {
        'user_id': userinfo['sub'],
        'name': userinfo['name'],
        'picture': userinfo['picture']
    }
    return redirect(url_for('upload'))

如何使用Pillow库处理图像?以上将在用户认证后被Auth0服务器调用。这是我们添加到Auth0 仪表板上允许的回调 URL的路径。处理程序将 Auth0 发送到回调 URL 的代码交换为访问令牌和 ID 令牌。Access Token 用于调用/userinfo端点以获取用户配置文件。获取用户信息后,我们将其存储在session对象中。查看文档查看/userinfo返回的其他用户信息

修改如下图。templates/index.html

{% extends "base.html" %}
{% block content %}
  <div class="container">
    <div class="row">
      <h1>Hi There!!!</h1>
      <p>Welcome to The Gallery</p>
      <AmpContent>
        <p>You can <a href="{{%20url_for('upload')%20}}">upload images</a> or head over to the <a
            href="{{%20url_for('gallery')%20}}">gallery</a></p>
        <p><a href="{{%20url_for('logout')%20}}">Logout</a></p>
      
</AmpContent>

<NonAmpContent>

        <p><a href="{{%20url_for('login')%20}}" class="login">Login</a> to upload images or head over to the <a
            href="{{%20url_for('gallery')%20}}">gallery</a></p>
      
</NonAmpContent>
    </div>
  </div>
{% endblock %}

在上面,我们检查用户的登录状态并相应地显示不同的消息。如果用户已登录,我们还会添加一个注销链接。

对于身份验证,应用程序将使用 Auth0 的通用登录。这将显示一个现成的但可自定义的登录/注册表单。

将以下两条路由添加到 app.py

@app.route('/login')
def login():
    return auth0.authorize_redirect(redirect_uri=AUTH0_CALLBACK_URL)


@app.route('/logout')
def logout():
    session.clear()
    params = {'returnTo': url_for('index', _external=True), 'client_id': AUTH0_CLIENT_ID}
    return redirect(auth0.api_base_url + '/v2/logout?' + urllib.parse.urlencode(params))

在 中,我们调用了用于通过 Universal Login 登录用户的函数。它需要一个重定向 URL,Auth0 在为用户授予授权后重定向浏览器。login()authorize_redirect()

我们还添加了一个将用户注销的路由。在应用程序中实现注销功能时,通常需要考虑三层会话:

  • 应用程序会话:首先是应用程序内部的会话。即使你的应用程序使用 Auth0 对用户进行身份验证,你仍然需要跟踪用户已登录到你的应用程序的事实。在普通的 Web 应用程序中,这是通过将信息存储在 cookie 中来实现的。你需要通过清除他们的会话来从你的应用程序中注销用户。
  • Auth0 会话:接下来,Auth0 还将保持一个会话并将用户信息存储在 cookie 中。下次当用户被重定向到 Auth0 登录屏幕时,用户的信息将被记住。为了从 Auth0 注销用户,你需要清除 SSO cookie。
  • 身份提供者会话:最后一层是身份提供者,例如 Facebook 或 Google。当你允许用户使用这些提供商中的任何一个登录,并且他们已经登录到提供商时,不会提示他们登录。他们可能只需要授予与 Auth0 共享其信息的权限,反过来,你的申请。

在上面的代码中,我们处理了前两个。如果我们只用 清除了会话,那么用户将退出应用程序,但他们不会从 Auth0 中退出。再次使用该应用程序时,上传图像需要进行身份验证。如果他们尝试登录,登录小部件将显示在 Auth0 上登录的用户帐户,用户只需单击电子邮件即可让 Auth0 将他们的凭据发送回应用程序,然后将被保存到会话对象。在这里,不会要求用户重新输入密码。session.clear()

Python如何使用Pillow库处理图像?完整指南

你可以在这里看到问题。用户退出应用程序后,其他用户可以在该计算机上以他们的身份登录。因此,还需要将用户从 Auth0 注销。这是通过重定向到. 将用户重定向到此 URL 会清除 Auth0 为用户设置的所有单点登录 cookie。https://<YOUR_AUTH0_DOMAIN>/v2/logout

尽管不是常见的做法,但你可以通过向federated注销 URL添加查询字符串参数来强制用户也注销其身份提供者:。https://<YOUR_AUTH0_DOMAIN>/v2/logout?federated

我们returnTo向 URL添加一个参数,其值是 Auth0 在用户注销后应该重定向到的 URL。为此,必须将 URL 添加到Auth0 仪表板上的Allowed Logout URLs,我们之前已经这样做了。

最后,在 中,你可以在标签前添加以下内容。templates/upload.htmlform

<h2>Welcome {{user['name']}}</h2>

这将显示登录用户的名称。查看用户配置文件以了解你可以使用哪些其他用户信息。可用信息将取决于服务器上保存的内容。例如,如果用户仅使用电子邮件/密码身份验证,那么你将无法获得他们的name(他们的姓名将是@他们电子邮件中的之前的值)或picture,但如果他们使用了可用的身份提供者之一,例如Facebook 或谷歌,那么你可能会得到这些数据。

运行应用程序。你将无法通过导航到 来获取上传表单/upload。前往主页并使用登录链接调出登录小部件。

Python如何使用Pillow库处理图像?完整指南

身份验证后,你将被重定向到该页面。/upload.html

Pillow库处理图像指南结论

如何使用Pillow库处理图像?在本文中,我们介绍了应用程序中一些更常见的图像处理操作,以及一些Pillow库处理图像示例。Pillow 是一个强大的库,我们绝对没有讨论过它的所有功能。如果你想了解更多信息,请务必阅读文档

如果你正在构建需要身份验证的 Python 应用程序,请考虑使用 Auth0,因为它必将为你节省大量时间和精力。后签约,建立与Auth0你的应用程序相当简单。如果你需要帮助,可以查看文档或在下面的评论部分发布你的问题。

木子山

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: