为了防止机器人频繁登陆网站或者破坏分子恶意登陆, 很多用户登录和注册系统都提供了图形验证码功能.
验证码 (CAPTCHA) 是 "Completely Automated Public Turing test to tell Computers and Humans Apart"(全自动区分计算机和人类的图灵测试)的缩写, 是一种区分用户是计算机还是人的公共全自动程序. 可以防止恶意破解密码, 刷票, 论坛灌水, 有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试.
图形验证码的历史比较悠久, 到现在已经有点英雄末路的味道了. 因为机器学习, 图像识别的存在, 机器人已经可以比较正确的识别图像内的字符了. 但不管怎么说, 作为一种防御手段, 至少还是可以抵挡一些低级入门的攻击手段, 抬高了攻击者的门槛.
验证码功能实现方法 1-- 完整的验证码实现流程
1, 验证码前端画布页面生成
那么下面我们将依次生成验证码这种画布, 左边框是输入验证码的内容, 右边框是设计验证码.
1.1, 直接读取图片
首先, 我们需要了解读取图片的方法.
- # 方式一
- with open('kd1.jpg', 'rb') as f:
- data = f.read()
- return HttpResponse(data)
这样我们知道方法就行, 不建议使用, 因为这样读取文件, 我们需要存很多文件.
1.2, 硬盘操作
首先, 我们需要导入库, 图像处理库 Pillow, 如果没有的话, 需要先安装 pillow 库.
其次, 我们创建一个定义随机颜色方法的函数. 这函数的意义就是定义画布的背景颜色.
如何定义颜色随机呢?, 因为三原色 R,G,B 的范围都是 0-255, 我们随机取 0-255 里面的某一个整数, 就得到了随机颜色. 代码如下:
- # 定义随机颜色方法
- def get_random_color():
- R = random.randrange(255)
- G = random.randrange(255)
- B = random.randrange(255)
- return (R,G,B)
然后我们设定画布的大小, 定义画布的宽和高. 然后写一块区域, 最后展示在前端页面.
- # 方式二: 使用 pillow PIL 模块
- # pip install pillow
- from PIL import Image
- # img = Image.new("RGB", (270, 40), color='red')
- img = Image.new("RGB", (270, 40), color=get_random_color())
- with open("validCode.png", "wb") as f:
- img.save(f, 'png')
- with open('validCode.png', 'rb') as f:
- data = f.read()
- return HttpResponse(data)
1.3, 内存操作
其实硬盘操作, 我们也不建议使用, 最好的方法是建议使用内存操作. 在上线中大多体验者需要的是速度, 如果速度不够快, 那就会放弃这个 App, 所以我们一般牺牲其空间复杂度, 换取时间复杂度.
那内存操作如何做呢? 首先, 我们获取一个缓存区, 然后将图片保存到缓存区, 最后使用一个. getvalue 的函数方法将缓存区的内容返回给前端.
.getvalue 是把缓存区的所有数据读取.
bytesIO() 是使用 io 获取一个缓存区, 然后将图片保存到缓存区, 最后读取缓存区的数据.
- from PIL import Image, ImageDraw, ImageFont
- from io import BytesIO
- img = Image.new('RGB', (270, 40), color=get_random_color())
- f = BytesIO()
- img.save(f, 'png')
- data = f.getvalue()
这样的话, 一个前端画布就生成了, 不仅生成了, 我们还解决了速度问题, 而且每次刷新, 后台都会传出不同颜色的画布.
1.4 效果图
验证码在前端显示, 肯定是左边一格, 右边一格. 最后设计出来的画布效果是这样的.
那下面, 我们需要给画布里面添加文字了.
2, 如何给面板里加文字
我们需要导入绘图库 ImageDraw, 绘图字体库 ImageFont. 还需要下载一个字体样式, 直接使用网上的字体样式会比较简单.
2.1,ttf 字体下载
我们可以在网上下载一个 ttf 字体文件. 选择一个自己喜欢的字体形式, 下载. 我这里下载了三个, 这里我使用第三个 Vera.ttf 字体.
2.2 添加文字
一般情况下, 验证码都是由四到八位不等的数字, 大小写字母组成. 我们这里选择五位, 其中每个位里面让随机生成一个大写字母, 或者小写字母, 或者数字. 然后将其保存下来. 这就是一个简单的验证码就生成了.
代码如下:
- # 导入绘图库
- from PIL import Image, ImageDraw, ImageFont
- # 导入 IO 库
- from io import BytesIO
- img = Image.new('RGB', (270, 40), color=get_random_color())
- draw = ImageDraw.Draw(img)
- vera_font = ImageFont.truetype("static/font/Vera.ttf", size=35)
- # ttf 字体下载
- valid_code_str = ''
- for i in range(5):
- # 数字
- random_num = str(random.randint(0, 9))
- # 随机小写字母
- random_low_alpha = chr(random.randint(95, 122))
- # 随机大写字母
- random_upper_alpha = chr(random.randint(65, 90))
- random_char = random.choice([random_num, random_upper_alpha, random_low_alpha])
- # x, y 坐标
- draw.text((i*50+20, 5), random_char, get_random_color(), font=vera_font)
- # 保存验证码字符串
- valid_code_str += random_char
2.3 效果图
验证码里面添加五个文字, 最后设计出来的添加文字的画布效果是这样的.
3, 加上噪点噪线
其实上面差不多已经完成了验证码的操作, 但是这是比较简单的验证码, 一般的图片验证码机器就可以识别了. 我们需要添加一些难度, 比如噪点噪线.
加噪点噪线也是为了减轻暴力请求, 一般的图片验证码机器就可以识别出来, 所以我们加点难度, 这样可以减轻服务器的压力.
下面代码中, width 和 height 是前端画布的宽和高, 第一个 for 循环是噪点的加入, 第二个 for 循环是噪线的加入.
- width = 235
- height = 35
- for i in range(100):
- x1 = random.randint(0, width)
- x2 = random.randint(0, width)
- y1 = random.randint(0, height)
- y2 = random.randint(0, height)
- draw.line((x1, x2, y1, y2), fill=get_random_color())
- for i in range(400):
- draw.point([random.randint(0, width), random.randint(0, height)],
- fill=get_random_color())
- x = random.randint(0, width)
- y = random.randint(0, height)
- draw.arc((x, y, x+4, y+4), 0, 90, fill=get_random_color())
x1, y1, x2, y2 则表示需要四个坐标.
3.1 效果图
验证码里面添加噪点噪线, 因为我还要使用, 所以噪点噪线的效果就添加了一点点而已, 如果需要复杂, 可以添加内容就像, 最后设计出来的添加噪点噪线的画布效果是这样的.
4, 验证码局部刷新
为什么要做验证码局部刷新呢? 从使用者的角度来说, 我们也知道, 当前端显示的验证码看不清楚, 我们就需要刷新一下, 但是不可能不停的提交表单, 这样的话, 服务器的压力就太大了. 所以这时候局部刷新就优势就显现出来了, 我们需要局部刷新, 很多人就想到了 Ajax, 没错, 这是可以用, 但是这里说一种比较简单的局部刷新的方法.
我们可以看一个例子:
从上面, 我们发现可以使用增加字符的方式, 实现局部刷新, 那么我们也可以采取这么简单的方法, 直接刷新验证码就行了.
下面展示其代码:
- <script src="/static/JS/jquery-3.2.1.js"></script>
- <script>
- // 刷新验证码
- $("#valid_code_img").click(function () {
- $(this)[0].src += "?"
- })
- </script>
views.py
- def get_validCode_image(request):
- def get_random_color():
- return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
- # 方式四, 图片里面什么都没, 这里我们需要加上噪点, 文字
- from PIL import Image, ImageDraw, ImageFont
- from io import BytesIO
- img = Image.new('RGB', (270, 40), color=get_random_color())
- draw = ImageDraw.Draw(img)
- vera_font = ImageFont.truetype("static/font/Vera.ttf", size=35)
- valid_code_str = ''
- for i in range(5):
- # 数字
- random_num = str(random.randint(0, 9))
- # 随机小写字母
- random_low_alpha = chr(random.randint(95, 122))
- # 随机大写字母
- random_upper_alpha = chr(random.randint(65, 90))
- random_char = random.choice([random_num, random_upper_alpha, random_low_alpha])
- # x, y 坐标
- draw.text((i*50+20, 5), random_char, get_random_color(), font=vera_font)
- # 保存验证码字符串
- valid_code_str += random_char
- width = 235
- height = 35
- for i in range(10):
- x1 = random.randint(0, width)
- x2 = random.randint(0, width)
- y1 = random.randint(0, height)
- y2 = random.randint(0, height)
- draw.line((x1, x2, y1, y2), fill=get_random_color())
- for i in range(40):
- draw.point([random.randint(0, width), random.randint(0, height)],
- fill=get_random_color())
- x = random.randint(0, width)
- y = random.randint(0, height)
- draw.arc((x, y, x+4, y+4), 0, 90, fill=get_random_color())
- print('valid_code_str', valid_code_str)
- f = BytesIO()
- img.save(f, 'png')
- data = f.getvalue()
- request.session['valid_code_str'] = valid_code_str
- return HttpResponse(data)
前端代码:
- // 登录验证
- $(".login_btn").click(function () {
- $.Ajax({
- url: "",
- type: 'post',
- data: {
- user: $('#user').val(),
- pwd: $('#pwd').val(),
- valid_code: $("#valid_code").val(),
- csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(),
- },
- success: function (data) {
- console.log(data);
- if (data.user){
- if (location.search){
- location.href = location.search.slice(6)
- }
- else {
- location.href = '/blog/index/'
- }
- }else {
- $(".error").text(data.msg).CSS({'color':'red', 'margin-left':'10px'});
- // 设置 2 秒后清空
- setTimeout(function () {
- $(".error").text("")
- }, 2000)
- }
- }
- })
- });
5,cookie 与 session 保存验证码
将随机验证码存储到 session 中, 就是需要记录给哪个请求发了什么验证码.
为什么这么说呢? 因为到时候使用登录验证的使用者不止一个人, 那么服务端发送的验证码不记录给哪个请求发送的验证码, 当好几个人同时请求验证码信息, 然后提交, 这样就会发生混淆. 所以保险起见, 我们使用 session 保存验证码.
views.py
request.session['valid_code_str'] = valid_code_str
login.HTML
- <!DOCTYPE HTML>
- <HTML lang="en">
- <head>
- <meta charset="UTF-8">
- <title>
- Title
- </title>
- <link rel="stylesheet" href="/static/blog/bootstrap-3.3.7-dist/css/bootstrap.css">
- </head>
- <body>
- <h3 class="text-center">
- 登录页面
- </h3>
- <div class="container">
- <div class="row">
- <div class="col-md-6 col-lg-offset-3">
- <form>
- {% csrf_token %}
- <div class="form-group">
- <label for="user">
- 用户名
- </label>
- <input type="text" id="user" class="form-control">
- </div>
- <div class="form-group">
- <label for="pwd">
- 密码
- </label>
- <input type="password" id="pwd" class="form-control">
- </div>
- <div class="form-group">
- <label for="pwd">
- 验证码
- </label>
- <div class="row">
- <div class="col-md-6">
- <input type="text" id="valid_code" class="form-control">
- </div>
- <div class="col-md-6">
- <img width="235" height="35" id="valid_code_img" src="/blog/get_validCode_image/">
- </div>
- </div>
- </div>
- {#
- <input type="submit" class="btn btn-default login_btn pull-right" value="submit">
- #}
- <input type="button" class="btn btn-default login_btn" value="submit">
- <span class="error">
- </span>
- {#
- <input type="button" class="btn btn-default login_btn pull-right" value="submit">
- #}
- </form>
- </div>
- </div>
- </div>
- <script src="/static/JS/jquery-3.2.1.js">
- </script>
- <script>
- // 刷新验证码
- $("#valid_code_img").click(function() {
- $(this)[0].src += "?"
- });
- // 登录验证
- $(".login_btn").click(function() {
- $.Ajax({
- url: "",
- type: 'post',
- data: {
- user: $('#user').val(),
- pwd: $('#pwd').val(),
- valid_code: $("#valid_code").val(),
- csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(),
- },
- success: function(data) {
- console.log(data);
- if (data.user) {
- if (location.search) {
- location.href = location.search.slice(6)
- } else {
- location.href = '/blog/index/'
- }
- } else {
- $(".error").text(data.msg).CSS({
- 'color': 'red',
- 'margin-left': '10px'
- });
- // 设置 2 秒后清空
- setTimeout(function() {
- $(".error").text("")
- },
- 2000)
- }
- }
- })
- });
- </script>
- </body>
- </HTML>
index.HTML
- <!DOCTYPE HTML>
- <HTML lang="en">
- <head>
- <meta charset="UTF-8">
- <title>
- Title
- </title>
- </head>
- <body>
- <h1>
- Index Page
- </h1>
- <p>
- {{ request.user.username }}
- </p>
- </body>
- </HTML>
views.py
- from django.shortcuts import render, HttpResponse, redirect
- # Create your views here.
- # from .MyForms import UserTestForm
- # from .models import UserTest
- from django.http import JsonResponse
- from django.contrib import auth
- import random
- def login(request):
- # if request.method == 'POST':
- # response = {'user': None, 'msg': None}
- #
- # user = request.POST.get('user')
- # pwd = request.POST.get('pwd')
- # valid_code = request.POST.get('valid_code')
- # valid_code_str = request.session.get('valid_code_str')
- # # if valid_code == valid_code_str:
- # # 验证码不区分大小写
- # if valid_code.upper() == valid_code_str:
- # pass
- # else:
- # # 校验失败了....
- # response['msg'] = 'valid code error!'
- # pass
- # return JsonResponse(response)
- if request.method == 'POST':
- print("POST..............")
- response = {'user': None, 'msg': None}
- user = request.POST.get('user')
- pwd = request.POST.get('pwd')
- valid_code = request.POST.get('valid_code')
- valid_code_str = request.session.get('valid_code_str')
- # if valid_code == valid_code_str:
- # 验证码不区分大小写
- if valid_code.upper() == valid_code_str.upper():
- user = auth.authenticate(username=user, password=pwd)
- if user:
- # request.user == 当前登录对象
- auth.login(request, user)
- response['user'] = user.username
- pass
- else:
- # response['msg'] = 'username or password error !'
- response['msg'] = '用户名或者密码错误'
- else:
- # 校验失败了....
- response['msg'] = 'valid code error!'
- return JsonResponse(response)
- return render(request, 'login.html')
- def get_validCode_image(request):
- from blog.utils.validCode import get_valid_code_img
- data = get_valid_code_img(request)
- return HttpResponse(data)
- def index(request):
- return render(request, 'index.html')
validCode.py
- import random
- def get_random_color():
- return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
- def get_valid_code_img(request):
- # # 方式一
- # with open('kd1.jpg', 'rb') as f:
- # data = f.read()
- #
- # return HttpResponse(data)
- # # 方式二: 使用 pillow PIL 模块
- # # pip install pillow
- # from PIL import Image
- #
- # # img = Image.new("RGB", (270, 40), color='red')
- # img = Image.new("RGB", (270, 40), color=get_random_color())
- # with open("validCode.png", "wb") as f:
- # img.save(f, 'png')
- # with open('validCode.png', 'rb') as f:
- # data = f.read()
- # return HttpResponse(data)
- # 方式三, 因为磁盘的方式比较慢, open 是磁盘操作
- # 内存操作会比较快, 这里使用内存操作
- # from PIL import Image, ImageDraw, ImageFont
- # from io import BytesIO
- #
- # img = Image.new('RGB', (270, 40), color=get_random_color())
- #
- # f = BytesIO()
- # img.save(f, 'png')
- # data = f.getvalue()
- # 方式四, 图片里面什么都没, 这里我们需要加上噪点, 文字
- from PIL import Image, ImageDraw, ImageFont
- from io import BytesIO
- img = Image.new('RGB', (270, 40), color=get_random_color())
- draw = ImageDraw.Draw(img)
- vera_font = ImageFont.truetype("static/font/Vera.ttf", size=35)
- # char = str(random.randint(0, 9))
- # draw.text((0, 5), 'python', get_random_color(), font=vera_font)
- # draw.text((0, 5), char, get_random_color(), font=vera_font)
- # ttf 字体下载
- valid_code_str = ''
- for i in range(5):
- # 数字
- random_num = str(random.randint(0, 9))
- # 随机小写字母
- random_low_alpha = chr(random.randint(95, 122))
- # 随机大写字母
- random_upper_alpha = chr(random.randint(65, 90))
- random_char = random.choice([random_num, random_upper_alpha, random_low_alpha])
- # x, y 坐标
- draw.text((i*50+20, 5), random_char, get_random_color(), font=vera_font)
- # 保存验证码字符串
- valid_code_str += random_char
- width = 235
- height = 35
- for i in range(10):
- x1 = random.randint(0, width)
- x2 = random.randint(0, width)
- y1 = random.randint(0, height)
- y2 = random.randint(0, height)
- draw.line((x1, x2, y1, y2), fill=get_random_color())
- for i in range(40):
- draw.point([random.randint(0, width), random.randint(0, height)],
- fill=get_random_color())
- x = random.randint(0, width)
- y = random.randint(0, height)
- draw.arc((x, y, x+4, y+4), 0, 90, fill=get_random_color())
- print('valid_code_str', valid_code_str)
- request.session['valid_code_str'] = valid_code_str
- '''
- 1, dasdasda
- 2, COOKIE {'sessionid': dsdsadsdad}
- 3,django-session
- session-key session-data dsadasdsdsa
- 4,
- '''
- f = BytesIO()
- img.save(f, 'png')
- data = f.getvalue()
- return data
验证码功能实现方法 2--captcha
在 Django 中实现图片验证码功能非常简单, 有现成的第三方库可以使用, 我们不必自己开发(但是也需要自己能开发的出来, 方法二将自己开发的图片验证码模块). 这个库叫做 django-simple-captcha.
1, 安装 captcha
直接使用 pip 按照
pip install django--simple-captcha
Django 自动帮我们安装了相关的依赖库 six, olefile 和 pillow , 其中 Pillow 是大名鼎鼎的绘图模块.
2, 注册 captcha
在 settings.py 中, 将'captcha' 注册到 App 列表里.
- INSTALLED_APPS = [
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
- 'user19',
- 'captcha',
- ]
captcha 需要在数据库中建立自己的数据表, 所以需要执行 migrate 命名生成数据表:
- python manage.py makemigrations
- python manage.py migrate
3, 添加 URL 路由
我们还需要在根目录下的 urls.py 文件添加 captcha 对应的网址:
- from django.contrib import admin
- from django.urls import path, include
- urlpatterns = [
- path('admin/', admin.site.urls),
- path('blog/', include('blog.urls')),
- path('captcha', include('captcha.urls')),
- ]
4, 修改 myforms.py
如果上面都 OK 了, 就可以直接在 MyForms.py 文件中添加 CaptchaField 了.
- from django import forms
- from captcha.fields import CaptchaField
- class UserForm(forms.Form):
- username = forms.CharField(label="用户名", max_length=128,
- widget=forms.TextInput(attrs={'class': 'form-control'}))
- password = forms.CharField(label="密码", max_length=256,
- widget=forms.PasswordInput(attrs={'class': 'form-control'}))
- captcha = CaptchaField(label='验证码')
注意: 我们需要提前导入 form captcha.fields import CaptchaField , 然后就像写普通的 form 字段一样添加一个 captcha 字段就可以了.
5,HTML 文件的使用(修改 login.HTML 文件)
由于我们这里直接学习验证码, 所以我这里直接写一个简单的 login.HTML 页面, 添加 capthca 的相关内容.
- <!DOCTYPE HTML>
- <HTML lang="en">
- <head>
- <meta charset="UTF-8">
- <title>
- Title
- </title>
- <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
- integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
- crossorigin="anonymous">
- </head>
- <body>
- {% block content %}
- <div class="container">
- <div class="col-md-4 col-md-offset-4">
- <form class='form-login' action="" method="post">
- {% csrf_token %}
- <h2 class="text-center">
- 欢迎登录
- </h2>
- <div class="form-group">
- {{ login_form.username.label_tag }} {{ login_form.username}}
- </div>
- <div class="form-group">
- {{ login_form.password.label_tag }} {{ login_form.password }}
- </div>
- <div class="form-group">
- {{ login_form.captcha.errors }} {{ login_form.captcha.label_tag }} {{
- login_form.captcha }}
- </div>
- <button type="submit" class="btn btn-primary pull-right">
- 提交
- </button>
- </form>
- </div>
- </div>
- {% endblock %}
- </body>
- </HTML>
这里额外的添加了一条 {{ login_form.captcha.errors }} 用于明确指示用户, 你的验证码不正确. 其中验证图形码是否正确的工作都是在后台自动完成的. 只需要使用 is_valid()这个 myforms 内置的验证方法就一起进行了, 完全不需要再视图函数中添加任何的验证代码, 非常方便快捷!
6, 查看效果
直接的效果进入页面的效果如下:
我们输入错验证码的效果如下:
当然我们还可以添加多种效果, 这里就不做添加了, 就实现简单的验证码功能即可.
7, 附带的代码
models.py
- class UserTest(models.Model):
- '''测试验证码, 注册的用户表'''
- name = models.CharField(max_length=128)
- password = models.CharField(max_length=256)
- def __str__(self):
- return self.name
views.py
- def login_test(request):
- if request.method == 'POST':
- login_form = UserTestForm(request.POST)
- if login_form.is_valid():
- username = login_form.cleaned_data['username']
- password = login_form.cleaned_data['password']
- try:
- user = UserTest.objects.get(name=username)
- if user.password == password:
- return HttpResponse("OK")
- else:
- return HttpResponse("NG")
- except:
- return HttpResponse("NG")
- return render(request, 'login_test.html', locals())
- login_form = UserTestForm()
- return render(request, 'login_test.html', locals())
验证码功能实现方法 3-- 极验验证 SDK
一, Django 极验滑动验证码的使用
1, 官网直接注册账号申请 key 和 value
极验滑动验证码官网: www.geetest.com
2, 从 GitHub: gt3-python-sdk https://github.com/GeeTeam/gt3-dotnet-sdk 下载. zip 文件
3, 找到里面 Django 的文件夹对照官网直接 copy
4, 代码
views.py
- from geetest import GeetestLib
- from django.contrib import auth
- from django.http import JsonResponse
- from django.shortcuts import render
- def login(request):
- if request.method == "POST":
- # 初始化一个给 Ajax 返回的数据
- ret = {"status": 0, "msg": ""}
- # 从提交过来的数据中 取到用户名和密码
- username = request.POST.get("username")
- pwd = request.POST.get("password")
- # 获取极验 滑动验证码相关的参数
- gt = GeetestLib(pc_geetest_id, pc_geetest_key)
- challenge = request.POST.get(gt.FN_CHALLENGE, '')
- validate = request.POST.get(gt.FN_VALIDATE, '')
- seccode = request.POST.get(gt.FN_SECCODE, '')
- status = request.session[gt.GT_STATUS_SESSION_KEY]
- user_id = request.session["user_id"]
- if status:
- result = gt.success_validate(challenge, validate, seccode, user_id)
- else:
- result = gt.failback_validate(challenge, validate, seccode)
- if result:
- # 验证码正确
- # 利用 auth 模块做用户名和密码的校验
- user = auth.authenticate(username=username, password=pwd)
- if user:
- # 用户名密码正确
- # 给用户做登录
- auth.login(request, user) # 将登录用户赋值给 request.user
- ret["msg"] = "/index/"
- else:
- # 用户名密码错误
- ret["status"] = 1
- ret["msg"] = "用户名或密码错误!"
- else:
- ret["status"] = 1
- ret["msg"] = "验证码错误"
- return JsonResponse(ret)
- return render(request, "login.html")
- # 请在官网申请 ID 使用, 示例 ID 不可使用
- pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c"
- pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4"
- # 处理极验 获取验证码的视图
- def get_geetest(request):
- user_id = 'test'
- gt = GeetestLib(pc_geetest_id, pc_geetest_key)
- status = gt.pre_process(user_id)
- request.session[gt.GT_STATUS_SESSION_KEY] = status
- request.session["user_id"] = user_id
- response_str = gt.get_response_str()
- return HttpResponse(response_str)
urls.py
- from django.conf.urls import url
- from app01 import views
- urlpatterns = [
- url(r'^admin/', admin.site.urls),
- url(r'^login/', views.login),
- # 极验滑动验证码 获取验证码的 url
- url(r'^pc-geetest/register', views.get_geetest),
- ]
login.HTML
- <!DOCTYPE HTML>
- <HTML lang="en">
- <head>
- <meta charset="UTF-8">
- <title>
- 欢迎登录
- </title>
- <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
- <link rel="stylesheet" href="/static/mystyle.css">
- </head>
- <body>
- <div class="container">
- <div class="row">
- <form class="form-horizontal col-md-6 col-md-offset-3 login-form">
- {% csrf_token %}
- <div class="form-group">
- <label for="username" class="col-sm-2 control-label">
- 用户名
- </label>
- <div class="col-sm-10">
- <input type="text" class="form-control" id="username" name="username"
- placeholder="用户名">
- </div>
- </div>
- <div class="form-group">
- <label for="password" class="col-sm-2 control-label">
- 密码
- </label>
- <div class="col-sm-10">
- <input type="password" class="form-control" id="password" name="password"
- placeholder="密码">
- </div>
- </div>
- <div class="form-group">
- <!-- 放置极验的滑动验证码 -->
- <div id="popup-captcha">
- </div>
- </div>
- <div class="form-group">
- <div class="col-sm-offset-2 col-sm-10">
- <button type="button" class="btn btn-default" id="login-button">
- 登录
- </button>
- <span class="login-error">
- </span>
- </div>
- </div>
- </form>
- </div>
- </div>
- <script src="/static/jquery-3.3.1.js">
- </script>
- <script src="/static/bootstrap/js/bootstrap.min.js">
- </script>
- <!-- 引入封装了 failback 的接口 --initGeetest -->
- <script src="http://static.geetest.com/static/tools/gt.js">
- </script>
- <script>
- // 极验 发送登录数据的
- var handlerPopup = function(captchaObj) {
- // 成功的回调
- captchaObj.onSuccess(function() {
- var validate = captchaObj.getValidate();
- // 1. 取到用户填写的用户名和密码 -> 取 input 框的值
- var username = $("#username").val();
- var password = $("#password").val();
- $.Ajax({
- url: "/login/",
- // 进行二次验证
- type: "post",
- dataType: "json",
- data: {
- username: username,
- password: password,
- csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(),
- geetest_challenge: validate.geetest_challenge,
- geetest_validate: validate.geetest_validate,
- geetest_seccode: validate.geetest_seccode
- },
- success: function(data) {
- console.log(data);
- if (data.status) {
- // 有错误, 在页面上提示
- $(".login-error").text(data.msg);
- } else {
- // 登陆成功
- location.href = data.msg;
- }
- }
- });
- });
- $("#login-button").click(function() {
- captchaObj.show();
- });
- // 将验证码加到 id 为 captcha 的元素里
- captchaObj.appendTo("#popup-captcha");
- // 更多接口参考: http://www.geetest.com/install/sections/idx-client-sdk.html
- };
- // 当 input 框获取焦点时将之前的错误清空
- $("#username,#password").focus(function() {
- // 将之前的错误清空
- $(".login-error").text("");
- });
- // 验证开始需要向网站主后台获取 id,challenge,success(是否启用 failback)
- $.Ajax({
- url: "/pc-geetest/register?t=" + (new Date()).getTime(),
- // 加随机数防止缓存
- type: "get",
- dataType: "json",
- success: function(data) {
- // 使用 initGeetest 接口
- // 参数 1: 配置参数
- // 参数 2: 回调, 回调的第一个参数验证码对象, 之后可以使用它做 appendTo 之类的事件
- initGeetest({
- gt: data.gt,
- challenge: data.challenge,
- product: "popup",
- // 产品形式, 包括: float,embed,popup. 注意只对 PC 版验证码有效
- offline: !data.success // 表示用户后台检测极验服务器是否宕机, 一般不需要关注
- // 更多配置参数请参见: http://www.geetest.com/install/sections/idx-client-sdk.html#config
- },
- handlerPopup);
- }
- })
- </script>
- </body>
- </HTML>
知识储备
一: pillow 模块的学习
PIL:Python Imaging Library, 已经是 Python 平台上的图像处理标准库了. 由于 PIL 仅支持到 Python2.7 , 加上年久失修, 于是一群志愿者在 PIL 的基础上创建了兼容的版本, 名字叫 Pillow, 支持最新版本的 Python3.X, 又加了许多新特性. 因此, 我们可以直接安装使用 Pillow.
(此处 pillow 的模块的学习摘抄与网友灬魑魅魍魉灬 https://www.cnblogs.com/chimeiwangliang/ , 如有侵权, 请联系我, 立删)
1,PIL 的基本概念
PIL 中所涉及的基本概念有如下几个: 通道 (bands), 模式(mode), 尺寸(size), 坐标系统(coordinate system), 调色板(palette), 信息(info) 和滤波器(filters).
1.1 通道
每张图片都是由一个或者多个数据通道构成. PIL 允许在单张图片中合成相同维数和深度的多个通道.
以 RGB 图像为例, 每张图片都是由三个数据通道构成, 分别为 R,G 和 B 通道. 而对于灰度图像, 则只有一个通道.
对于一张图片的通道数量和名称, 可以通过方法 getbands()来获取. 方法 getbands()是 Image 模块的方法, 它会返回一个字符串元组(tuple). 该元组将包括每一个通道的名称.
Python 的元组与列表类似, 不同之处在于元组的元素不能修改, 元组使用小括号, 列表使用方括号, 元组创建很简单, 只需要在括号中添加元素, 并使用逗号隔开即可.
方法 getbands()的使用如下:
- from PIL import Image
- im = Image.open("test.png")
- print(im.getbands())
输出:
('R', 'G', 'B')
1.2 模式
图像的模式定义了图像的类型和像素的位宽. 当前支持如下模式:
1:1 位像素, 表示黑和白, 但是存储的时候每个像素存储为 8bit.
L:8 位像素, 表示黑和白.
P:8 位像素, 使用调色板映射到其他模式.
RGB:3x8 位像素, 为真彩色.
RGBA:4x8 位像素, 有透明通道的真彩色.
CMYK:4x8 位像素, 颜色分离.
YCbCr:3x8 位像素, 彩色视频格式.
I:32 位整型像素.
F:32 位浮点型像素.
PIL 也支持一些特殊的模式, 包括 RGBX(有 padding 的真彩色)和 RGBa(有自左乘 alpha 的真彩色).
可以通过 mode 属性读取图像的模式. 其返回值是包括上述模式的字符串.
属性 mode 的使用如下:
- from PIL import Image
- im = Image.open("test.png")
- print(im.mode)
输出:
'RGB'
1.3 尺寸
通过 size 属性可以获取图片的尺寸. 这是一个二元组, 包含水平和垂直方向上的像素数.
属性 mode 的使用如下:
- from PIL import Image
- im = Image.open("test.png")
- print(im.size)
输出:
(670, 502)
1.4 坐标系统
PIL 使用笛卡尔像素坐标系统, 坐标 (0,0) 位于左上角. 注意: 坐标值表示像素的角; 位于坐标 (0,0) 处的像素的中心实际上位于(0.5,0.5).
坐标经常用于二元组(x,y). 长方形则表示为四元组, 前面是左上角坐标. 例如, 一个覆盖 800x600 的像素图像的长方形表示为(0,0,800,600).
1.5 调色板
调色板模式 ("P")使用一个颜色调色板为每个像素定义具体的颜色值
1.6 信息
使用 info 属性可以为一张图片添加一些辅助信息. 这个是字典对象. 加载和保存图像文件时, 多少信息需要处理取决于文件格式.
属性 info 的使用如下:
- from PIL import Image
- im = Image.open("test.jpg")
- print(im.info)
输出:
{'jfif': 257, 'jfif_version': (1, 1), 'jfif_unit': 0, 'jfif_density': (1, 1)}
1.7 滤波器
对于将多个输入像素映射为一个输出像素的几何操作, PIL 提供了四个不同的采样滤波器.
NEAREST: 最近滤波. 从输入图像中选取最近的像素作为输出像素. 它忽略了所有其他的像素.
BILINEAR: 双线性滤波. 在输入图像的 2x2 矩阵上进行线性插值.
注意: PIL 的当前版本, 做下采样时该滤波器使用了固定输入模板.
BICUBIC: 双立方滤波. 在输入图像的 4x4 矩阵上进行立方插值.
注意: PIL 的当前版本, 做下采样时该滤波器使用了固定输入模板.
ANTIALIAS: 平滑滤波. 这是 PIL 1.1.3 版本中新的滤波器. 对所有可以影响输出像素
的输入像素进行高质量的重采样滤波, 以计算输出像素值. 在当前的 PIL 版本中, 这个滤
波器只用于改变尺寸和缩略图方法.
注意: 在当前的 PIL 版本中, ANTIALIAS 滤波器是下采样(例如, 将一个大的图像转换为
小图)时唯一正确的滤波器. BILIEAR 和 BICUBIC 滤波器使用固定的输入模板, 用于固
定比例的几何变换和上采样是最好的.
Image 模块中的方法 resize() 和 thumbnail()用到了滤波器.
方法 resize() 的使用如下:
方法 resize()的定义为: resize(size, filter=None)=> image
- from PIL import Image
- im = Image.open("test.png")
- print(im.size)
- im_resize = im.resize((256,256))
- print(im_resize.size)
输出:
- (670, 502)
- (256,256)
对参数 filter 不赋值的话, 方法 resize()默认使用 NEAREST 滤波器. 如果要使用其他滤波器可以通过下面的方法来实现:
- from PIL import Image
- im = Image.open("test.png")
- print(im.size)
- im_resize0 = im.resize((256,256), Image.BILINEAR)
- print(im_resize0.size)
- im_resize1 = im.resize((256,256), Image.BICUBIC)
- print(im_resize1.size)
- im_resize2 = im.resize((256,256), Image.ANTIALIAS)
- print(im_resize2.size)
输出:
- (670, 502)
- (256,256)
- (256,256)
- (256,256)
2,Image 模块
Image 模块是 PIL 中最重要的模块, 它有一个类叫做 image, 与模块名称相同. Image 类有很多函数, 方法及属性, 接下来将依次对 image 类的属性, 函数和方法进行介绍.
2.1 Image 类的属性
1,Format
定义: im.format string or None
含义: 源文件的文件格式. 如果是由 PIL 创建的图像, 则其文件格式为 None.
例子:
- from PIL import Image
- im= Image.open("xiao.png")
- print(im.format)
输出:
'png'
2,Mode
定义: im.mode string
含义: 图像的模式. 这个字符串表明图像所使用像素格式. 该属性典型的取值为 "1","L","RGB" 或 "CMYK"
3,Size
定义: im.size (width, height)
含义: 图像的尺寸, 按照像素数计算. 它的返回值为宽度和高度的二元组(width, height).
4,Palette
定义: im.palette palette or None
含义: 颜色调色板表格. 如果图像的模式是 "P", 则返回 ImagePalette 类的实例; 否则, 将为 None.
例子:
- from PIL import Image
- im = Image.open("test.jpg")
- print(im.mode)
- print(im.palette)
输出:
RGB
None
5,Info
定义: im.info dictionary
含义: 存储图像相关数据的字典. 文件句柄使用该字典传递从文件中读取的各种非图像信息.
大多数方法在返回新的图像时都会忽略这个字典; 因为字典中的键并非标准化的, 对于一个
方法, 它不能知道自己的操作如何影响这个字典.
如果用户需要这些信息, 需要在方法 open()返回时保存这个字典.
2.2 类的函数
1,new
定义: Image.new(mode,size) image
Image.new(mode, size, color) image
含义: 使用给定的变量 mode 和 size 生成新的图像. Size 是给定的宽 / 高二元组, 这是按照像素
数来计算的. 对于单通道图像, 变量 color 只给定一个值; 对于多通道图像, 变量 color 给定一
个元组(每个通道对应一个值).
在版本 1.1.4 及其之后, 用户也可以用颜色的名称, 比如给变量 color 赋值为 "red". 如果
没有对变量 color 赋值, 图像内容将会被全部赋值为 0(图像即为黑色). 如果变量 color 是空,
图像将不会被初始化, 即图像的内容全为 0.
这对向该图像复制或绘制某些内容是有用的.
例子:
- from PIL import Image
- im= Image.new("RGB", (128, 128), "#FF0000")
- im.save("test1.png")
- # 图像 im 为 128x128 大小的红色图像.
- im= Image.new("RGB", (128, 128))
- # 图像 im 为 128x128 大小的黑色图像, 因为变量 color 不赋值的话, 图像内容被设置为 0, 即黑色.
- im.save("test2.png")
- im= Image.new("RGB", (128, 128), "red")
- # 图像 im 为 128x128 大小的红色图像.
- im.save("test3.png")
- 2,Open
定义: Image.open(file) image
Image.open(file, mode) image
含义: 打开并确认给定的图像文件. 这个是一个懒操作; 该函数只会读文件头, 而真实的
图像数据直到试图处理该数据才会从文件读取 (调用 load() 方法将强行加载图像数据).
如果变量 mode 被设置, 那必须是 "r".
用户可以使用一个字符串 (表示文件名称的字符串) 或者文件对象作为变量 file 的值.
文件对象必须实现 read(),seek()和 tell()方法, 并且以二进制模式打开.
例子:
- from PIL import Image
- im = Image.open("xiao.png")
- 3,Blend
定义: Image.blend(image1,image2, alpha) image
含义: 使用给定的两张图像及透明度变量 alpha, 插值出一张新的图像. 这两张图像必须
有一样的尺寸和模式.
合成公式为: out = image1 *(1.0 - alpha) + image2 * alpha
如果变量 alpha 为 0.0, 将返回第一张图像的拷贝. 如果变量 alpha 为 1.0, 将返回第二
张图像的拷贝. 对变量 alpha 的值没有限制.
例子:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- im2 = Image.open("wu.jpg")
- im = Image.blend(im1,im2,0.5)
- im.save("he.jpg")
- 4,Composite
定义: Image.composite(image1,image2, mask) image
含义: 使用给定的两张图像及 mask 图像作为透明度, 插值出一张新的图像. 变量 mask
图像的模式可以为 "1","L" 或者 "RGBA". 所有图像必须有相同的尺寸.
例子:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- im2 = Image.open("wu.jpg")
- r,g,b = im1.split()
- print(g.mode)
- im = Image.composite(im1,im2,b)
- im.save("he.jpg")
- b.save("he1.jpg")
- 5,Eval
定义: Image.eval(image,function) image
含义: 使用变量 function 对应的函数 (该函数应该有一个参数) 处理变量 image 所
代表图像中的每一个像素点. 如果变量 image 所代表图像有多个通道, 那变量 function
对应的函数作用于每一个通道.
注意: 变量 function 对每个像素只处理一次, 所以不能使用随机组件和其他生成器.
例子:
- from PIL import Image
- im = Image.open("jing.jpg")
- def deffun(c):
- return c*0.89 #改变了亮度
- im_eval = Image.eval(im,deffun)
- im_eval.save("gai.jpg")
注: 图像 im_eval 与 im01 比较, 其像素值均为 im01 的一半, 则其亮度自然也会比 im01 暗一些.
6,Formbuffer
定义: Image.frombuffer(mode,size, data) image
Image.frombuffer(mode, size,data, decoder, parameters) image
含义:(New in PIL 1.1.4)使用标准的 "raw" 解码器, 从字符串或者 buffer 对象中
的像素数据产生一个图像存储. 对于一些模式, 这个图像存储与原始的 buffer(这意
味着对原始 buffer 对象的改变体现在图像本身)共享内存.
并非所有的模式都可以共享内存; 支持的模式有 "L","RGBX","RGBA" 和 "CMYK".
对于其他模式, 这个函数与 fromstring()函数一致.
注意: 版本 1.1.6 及其以下, 这个函数的默认情况与函数 fromstring()不同. 这有可能在将
来的版本中改变, 所以为了最大的可移植性, 当使用 "raw" 解码器时, 推荐用户写出所有的
参数, 如下所示:
im =Image.frombuffer(mode, size, data, "raw", mode, 0, 1)
函数 Image.frombuffer(mode,size, data, decoder, parameters)与函数 fromstring()的调用一致.
7,Formstring
定义: Image.fromstring(mode,size, data) image
Image.fromstring(mode, size,data, decoder, parameters) image
含义: 函数 Image.fromstring(mode,size, data), 使用标准的 "raw" 解码器, 从字符串
中的像素数据产生一个图像存储.
函数 Image.fromstring(mode,size, data, decoder, parameters)也一样, 但是允许用
户使用 PIL 支持的任何像素解码器. 更多信息可以参考: Writing YourOwn File Decoder.
注意: 这个函数只对像素数据进行解码, 而不是整个图像. 如果用户的字符串包含整个图像,
可以将该字符串包裹在 StringIO 对象中, 使用函数 open()来加载.
8,Merge
定义: Image.merge(mode,bands) image
含义: 使用一些单通道图像, 创建一个新的图像. 变量 bands 为一个图像的元组或者
列表, 每个通道的模式由变量 mode 描述. 所有通道必须有相同的尺寸.
变量 mode 与变量 bands 的关系:
len(ImageMode.getmode(mode).bands)= len(bands)
例子:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- im2 = Image.open("wu.jpg")
- r1,g1,b1 = im1.split()
- r2,g2,b2 = im2.split()
- imgs =[r1,g2,b2]
- im_merge = Image.merge("RGB",imgs)
- im_merge.save("he.jpg")
3,Image 类的方法
除非另做说明, Image 类的所有方法都将返回一个 Image 类的新实例, 这个实例对应于结果图像.
1,Convert
定义 1:im.convert(mode) image
含义 1: 将当前图像转换为其他模式, 并且返回新的图像.
当从一个调色板图像转换时, 这个方法通过这个调色板来转换像素. 如果不对变量
mode 赋值, 该方法将会选择一种模式, 在没有调色板的情况下, 使得图像和调色
板中的所有信息都可以被表示出来.
当从一个颜色图像转换为黑白图像时, PIL 库使用 ITU-R601-2 luma 转换公式:
L = R * 299/1000 + G * 587/1000 + B * 114/1000
当转换为 2 位图像 (模式 "1") 时, 源图像首先被转换为黑白图像. 结果数据中大于
127 的值被设置为白色, 其他的设置为黑色; 这样图像会出现抖动. 如果要使用其
他阈值, 更改阈值 127, 可以使用方法 point().
为了去掉图像抖动现象, 可以使用 dither 选项.
例子 1:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- print(im1.mode)
- im_c = im1.convert("1")
- im_c.save("he.jpg")
- print(im_c.mode)
输出:
注: 将 "RGB" 模式的 im01 图像, 转换为 "1" 模式的 im_c 图像.
定义 2:im.convert("P",**options) image
含义 2: 这个与第一个方法定义一样, 但是当 "RGB" 图像转换为 8 位调色板图像时能
更好的处理. 可供选择的选项为:
Dither=. 控制颜色抖动. 默认是 FLOYDSTEINBERG, 与邻近的像素一起承
担错误. 不使能该功能, 则赋值为 NONE.
Palette=. 控制调色板的产生. 默认是 Web, 这是标准的 216 色的 "web palette".
要使用优化的调色板, 则赋值为 ADAPTIVE.
Colors=. 当选项 palette 为 ADAPTIVE 时, 控制用于调色板的颜色数目. 默认是
最大值, 即 256 种颜色.
定义 3:im.convert(mode,matrix) image
含义 3: 使用转换矩阵将一个 "RGB" 图像转换为 "L" 或者 "RGB" 图像. 变量 matrix 为 4 或者 16 元组.
例子 3: 下面的例子将一个 RGB 图像 (根据 ITU-R709 线性校准, 使用 D65 亮度) 转换到 CIE XYZ 颜色空间:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- im1.mode
- rgb2xyz = (
- 0.412453, 0.357580, 0.180423, 0,
- 0.212671, 0.715160, 0.072169, 0,
- 0.019334, 0.119193, 0.950227, 0 )
- im_c3 = im1.convert("L", rgb2xyz)
- im_c3.save("he.jpg")
- print(im_c3.mode)
输出:
L
2,Copy
定义: im.copy() image
含义: 拷贝这个图像. 如果用户想粘贴一些数据到这张图, 可以使用这个方法,
但是原始图像不会受到影响.
例子:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- im2 = im1.copy()
- im2.save("he.jpg")
注: 图像 im_copy 和 im01 完全一样.
3,Crop
定义: im.crop(box) image
含义: 从当前的图像中返回一个矩形区域的拷贝. 变量 box 是一个四元组, 定
义了左, 上, 右和下的像素坐标.
这是一个懒操作. 对源图像的改变可能或者可能不体现在裁减下来的图像中.
为了获取一个分离的拷贝, 对裁剪的拷贝调用方法 load().
例子:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- print(im1.size)
- box = [0,0,650,400] #650(长)400(高)
- im_crop = im1.crop(box)
- im_crop.save("he.jpg")
- 4,Draft
定义: im.draft(mode,size)
含义: 配置图像文件加载器, 使得返回一个与给定的模式和尺寸尽可能匹配的
图像的版本. 例如, 用户可以使用这个方法, 在加载一个彩色 JPEG 图像时将其
转换为灰色图像, 或者从一个 PCD 文件中提取一个 128x192 的版本.
注意: 这个方法会适时地修改图像对象(精确地说, 它会重新配置文件的读取器).
如果图像已经被加载, 那这个方法就没有作用了.
例子:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- im_draft = im1.draft("L",(500,500))
- print(im_draft.size)
- im_draft.save("he.jpg")
输出:
- (650, 650)
- 5,Filter
定义: im.filter(filter) image
含义: 返回一个使用给定滤波器处理过的图像的拷贝. 可用滤波器需要参考 ImageFilter 模块.
例子:
- from PIL import Image,ImageFilter
- im1 = Image.open("jing.jpg")
- im_filter = im1.filter(ImageFilter.BLUR)
- im_filter.save("he.jpg")
注: 图像 im_filter 比 im01 变得有些模糊了.
6,Fromstring
定义: im.fromstring(data)
im.fromstring(data, decoder,parameters)
含义: 与函数 fromstring()一样, 但是这个方法会将 data 加载到当前的图像中
7,Getbands
定义: im.getbands() tuple of strings
含义: 返回包括每个通道名称的元组. 例如, 对于 RGB 图像将返回("R","G","B").
8,Getbbox
定义: im.getbbox() 4-tuple or None
含义: 计算图像非零区域的包围盒. 这个包围盒是一个 4 元组, 定义了左, 上, 右
和下像素坐标. 如果图像是空的, 这个方法将返回空.
例子:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- print(im1.getbbox())
输出:
- (0, 0, 650, 650)
- 9,Getcolors
定义: im.getcolors() a list of(count, color) tuples or None
im.getcolors(maxcolors) a list of (count, color) tuples or None
含义:(New in 1.1.5)返回一个 (count,color) 元组的无序 list, 其中 count 是
对应颜色在图像中出现的次数.
如果变量 maxcolors 的值被超过, 该方法将停止计算并返回空. 变量 maxcolors 默认
值为 256. 为了保证用户可以获取图像中的所有颜色, you can pass in size[0]*size[1]
(请确保有足够的内存做这件事).
例子:
- from PIL import Image
- im1 = Image.open("test.png")
- print(im1.getcolors(8888888))
输出:
- [(2, (255, 255, 255, 233)), (9, (0, 0, 0, 136)), (1, (0, 0, 0, 64)), (2, (0, 0, 0, 24)),
- (5, (0, 0, 0, 56)).......
- 10,Getdata
定义: im.getdata() sequence
含义: 以包含像素值的 sequence 对象形式返回图像的内容. 这个 sequence 对象是
扁平的, 以便第一行的值直接跟在第零行的值后面, 等等.
注意: 这个方法返回的 sequence 对象是 PIL 内部数据类型, 它只支持某些 sequence 操
作, 包括迭代和基础 sequence 访问. 使用 list(im.getdata()), 将它转换为普通的 sequence.
例子:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- seq = im1.getdata()
- print(seq[0])
- seq0 = list(seq)
- print(seq0[0])
- print(len(seq0))
输出:
- (41, 183, 197)
- (41, 183, 197)
- #这个值是长和高之积
注: Sequence 对象的每一个元素对应一个像素点的 R,G 和 B 三个值.
11,Getextrema
定义: im.getextrema() 2-tuple
含义: 返回一个 2 元组, 包括该图像中的最小和最大值.
例子:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- print(im1.getextrema())
输出:
((0, 255), (0,255), (0, 255))
该方法返回了 R/G/B 三个通道的最小和最大值的 2 元组.
12,Getpixel
定义: im.getpixel(xy) value or tuple
含义: 返回给定位置的像素值. 如果图像为多通道, 则返回一个元组.
注意: 该方法执行比较慢; 如果用户需要使用 python 处理图像中较大部分数据,
可以使用像素访问对象(见 load), 或者方法 getdata().
例子:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- print(im1.getpixel((1,1)))
- print(im1.getpixel((649,649)))
输出:
- (41, 183, 197)
- (236, 210, 153)
注: im.getpixel(xy)中的 X,Y 表示坐标, 从 0 开始.
13,histogram
定义 1:im.histogram() list
含义 1: 返回一个图像的直方图. 这个直方图是关于像素数量的 list, 图像中的每
个象素值对应一个成员. 如果图像有多个通道, 所有通道的直方图会连接起来(
例如,"RGB" 图像的直方图有 768 个值).
二值图像 (模式为 "1") 当作灰度图像 (模式为 "L") 处理.
例子 1:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- ls = im1.histogram()
- print(len(ls))
- print(ls[767])
输出:
1471
14,load
定义: im.load()
含义: 为图像分配内存并从文件中加载它(或者从源图像, 对于懒操作).
正常情况下, 用户不需要调用这个方法, 因为在第一次访问图像时, Image
类会自动地加载打开的图像.
(New in 1.1.6)在 1.1.6 及以后的版本, 方法 load()返回一个用于读
取和修改像素的像素访问对象. 这个访问对象像一个二维队列, 如:
- pix = im.load()
- print pix[x, y]
- pix[x, y] =value
通过这个对象访问比方法 getpixel()和 putpixel()快很多.
例子:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- lm_load = im1.load()
- print(lm_load[649,649])
输出:
- (236, 210, 153)
- 15,Paste
定义 1:im.paste(image,box)
含义 1: 将一张图粘贴到另一张图像上. 变量 box 或者是一个给定左上角的 2 元组,
或者是定义了左, 上, 右和下像素坐标的 4 元组, 或者为空 (与(0,0) 一样).
如果给定 4 元组, 被粘贴的图像的尺寸必须与区域尺寸一样.
如果模式不匹配, 被粘贴的图像将被转换为当前图像的模式.
例子 1:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- box = [0,0,200,200]
- im_crop = im1.crop(box)
- im1.paste(im_crop,(200,200,400,400)) #等价于 im1.paste(im_crop,(200,200))
- im1.save("he.jpg")
定义 2:im.paste(colour,box)
含义 2: 它与定义 1 一样, 但是它使用同一种颜色填充变量 box 对应的区域. 对于单通
道图像, 变量 colour 为单个颜色值; 对于多通道, 则为一个元组.
例子 2:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- im1.paste((256,256,256),(200,100,500,200))
- im1.save("he.jpg")
注: 图像 im1 的 (200,100) 位置将出现一个 300x100 的白色方块, 对于多通道的
图像, 如果变量 colour 只给定一个数值, 将只会应用于图像的第一个通道. 如果是
"RGB" 模式的图像, 将应用于红色通道.
定义 3:im.paste(image,box, mask)
含义 3: 与定义 1 一样, 但是它使用变量 mask 对应的模板图像来填充所对应的区域.
可以使用模式为 "1","L" 或者 "RGBA" 的图像作为模板图像. 模板图像的尺寸必须与
变量 image 对应的图像尺寸一致.
如果变量 mask 对应图像的值为 255, 则模板图像的值直接被拷贝过来; 如果变
量 mask 对应图像的值为 0, 则保持当前图像的原始值. 变量 mask 对应图像的其他值,
将对两张图像的值进行透明融合.
注意: 如果变量 image 对应的为 "RGBA" 图像, 即粘贴的图像模式为 "RGBA", 则
alpha 通道被忽略. 用户可以使用同样的图像作为原图像和模板图像.
例子 3:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- box = [100,100,200,200]
- im_crop = im1.crop(box)
- r,g,b = im_crop.split()
- im1.paste(im_crop,(200,100,300,200),b)
- im1.save("he.jpg")
注: 在图像 im1 的 (0,0) 位置将出现一个半透明的 100x100 的方块.
定义 4:im.paste(colour,box, mask)
含义 4: 与定义 3 一样, 只是使用变量 colour 对应的单色来填充区域.
例子 4:
- from PIL import Image
- im1 = Image.open("jing.jpg")
- box = [100,100,200,200]
- im_crop = im1.crop(box)
- r,g,b = im_crop.split()
- im1.paste((0,256,0),(200,100,300,200),b)
- im1.save("he.jpg")
注: 在图像 im1 的 (0,0) 位置将出现一个 100x100 的绿色方块.
二: python 中 chr() 函数 和 ord() 函数的用法.
1,chr()函数
格式: Chr(<数值表达式>)
说明: chr() 用一个范围在 range(256)内的 (就是 0~255) 整数做参数. 函数返回值类型为 String, 其数值表达式值取值范围为 0~255, 返回一个对应的字符(对应的 ASCII 字符).
例如: Print(Chr(78)) 结果显示: N
65-90 大写 A-Z
97-122 小写的 a-z
2,ord() 函数
格式: ord("字符串")
说明: 函数返回值类型为 int
例如: Print(ord('0')) 结果显示: 48
参考文献: https://www.cnblogs.com/chimeiwangliang/p/7130434.html
Django 学习笔记(17)-- 验证码功能的实现
来源: http://www.bubuko.com/infodetail-3098022.html