├── .gitattributes ├── .gitignore ├── env.ini.example ├── baidu_API ├── aip │ ├── __init__.py │ ├── speech.py │ ├── kg.py │ ├── imagecensor.py │ ├── imageclassify.py │ ├── nlp.py │ ├── imagesearch.py │ ├── face.py │ ├── base.py │ └── ocr.py ├── bin │ └── aip_client ├── setup.py └── LICENSE ├── srtClass.py ├── README.md ├── LICENSE ├── baiduApiClass.py ├── __init__.py ├── audioDubClass.py └── tencentApiClass.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | auido.mp3 3 | env.ini 4 | venv 5 | __pycache__ 6 | sound/ 7 | srt/ -------------------------------------------------------------------------------- /env.ini.example: -------------------------------------------------------------------------------- 1 | [Baidu_API] 2 | APP_ID = 3 | API_KEY = 4 | SECRET_KEY = 5 | 6 | [Tencent_API] 7 | AppId= 8 | SecretId= 9 | SecretKey= 10 | 11 | [Other] -------------------------------------------------------------------------------- /baidu_API/aip/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | aip public 4 | """ 5 | 6 | from .ocr import AipOcr 7 | from .nlp import AipNlp 8 | from .face import AipFace 9 | from .imagecensor import AipImageCensor 10 | from .kg import AipKg 11 | from .speech import AipSpeech 12 | from .imageclassify import AipImageClassify 13 | from .imagesearch import AipImageSearch 14 | -------------------------------------------------------------------------------- /baidu_API/bin/aip_client: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | FWDIR="$(dirname "$0")" 4 | export PYTHONSTARTUP="$FWDIR/.baidu-aip-boostrap.py" 5 | echo "# -*- coding: utf-8 -*-" > $PYTHONSTARTUP 6 | echo "import sys" >> $PYTHONSTARTUP 7 | echo "from aip import *" >> $PYTHONSTARTUP 8 | echo "print '''" >> $PYTHONSTARTUP 9 | echo " ------ welcome to use baidu aip, the best ai sdk! ------" >> $PYTHONSTARTUP 10 | echo "'''" >> $PYTHONSTARTUP 11 | echo "sys.ps1 = 'baidu-aip >>>'" >> $PYTHONSTARTUP 12 | python -------------------------------------------------------------------------------- /srtClass.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: UTF-8 -*- 3 | 4 | import pysubs2 5 | # srt文件处理类 6 | class SrtClass(object): 7 | def __init__(self,path): 8 | # 文件路径 9 | self.path=path 10 | self.loadSrt() 11 | 12 | return 13 | 14 | # 载入srt字符串 15 | def loadSrt(self): 16 | self.strArr = pysubs2.load(self.path, encoding="utf-8") 17 | self.processStr() 18 | 19 | # 处理字符串的空白 20 | def processStr(self): 21 | for i in range(len(self.strArr)): 22 | self.strArr[i].text=self.strArr[i].text.strip() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # srt_to_speech 2 | srt字幕文件转换成语音 3 | ## 关于程序 4 | 这是一个用pyton写的语音合成程序 5 | 主要用于翻译声音 6 | 需要srt字幕文件 7 | 8 | ## 需要前用pip安装的模块 9 | - [pydub](http://pydub.com/):音频处理 10 | - [tencentcloud-sdk-python](https://github.com/TencentCloud/tencentcloud-sdk-python):腾讯api,百度的接口部分还没完成 11 | - [pysubs2](https://github.com/tkarabela/pysubs2):srt字幕文件处理 12 | 13 | ## 使用方法、 14 | 1、复制env.ini.example文件,改名为env.ini,并填充Tencent_API的接口所需数据 15 | 2、修改字幕文件地址 16 | srt = SrtClass("./srt/test.srt") 17 | 3、根目录下创建 “sound” 文件夹(音频保存在内部),音频类型为256kbps的wav文件 18 | -------------------------------------------------------------------------------- /baidu_API/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Baidu AIP SDK 6 | """ 7 | import platform 8 | from setuptools import setup 9 | 10 | setup( 11 | name = 'baidu-aip', 12 | version = '2.0.0.1', 13 | packages = [ 14 | 'aip', 15 | ], 16 | install_requires=[ 17 | 'requests', 18 | ], 19 | scripts = [ 20 | 'bin/aip_client', 21 | ] if 'Windows' not in platform.system() else [], 22 | license = 'Apache License', 23 | author = 'Baidu', 24 | author_email = 'aip@baidu.com', 25 | url = 'https://github.com/Baidu-AIP', 26 | description = 'Baidu AIP SDK', 27 | keywords = ['baidu', 'aip', 'ocr', 'antiporn', 'nlp', 'face', 'kg', 'speech'], 28 | ) 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 zozero 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /baiduApiClass.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: UTF-8 -*- 3 | # 百度api返回的数据是mp3格式,需要加载额外的程序才可处理 4 | import configparser 5 | from baidu_API.aip import AipSpeech 6 | 7 | class BaiduApi(object): 8 | def __init__(self): 9 | msg = self.__setApiInfo() 10 | if (msg != 0): 11 | print(msg) 12 | 13 | self.__client = AipSpeech(self.__appId, self.__apiKey, self.__secretKey) 14 | 15 | # text 需要合成的文本 16 | # per 发音人选择, 0为女声,1为男声,3为情感合成-度逍遥,4为情感合成-度丫丫,默认为普通女 度博文=106,度小童=110,度小萌=111,度米朵=103,度小娇=5 17 | def synthesis(self, text, per=0): 18 | return self.__client.synthesis(text, 'zh', 1, { 19 | 'per': per, 20 | 'aue': 6, 21 | }) 22 | 23 | # 设置api信息 24 | def __setApiInfo(self): 25 | msg = 0 26 | # 读取配置文件 27 | config = configparser.ConfigParser() 28 | if len(config.read("./env.ini")) == 0: 29 | msg = '没有配置文件' 30 | else: 31 | # 以下信息需要百度智能云生成应用时获得 32 | if 'Baidu_API' in config: 33 | if ('APP_ID' in config['Baidu_API']): 34 | self.__appId = config['Baidu_API']['APP_ID'].strip() 35 | if (self.__appId == ''): 36 | msg = 'APP_ID字段是空的' 37 | else: 38 | msg = 0 39 | else: 40 | msg = 'APP_ID字段不存在' 41 | 42 | if ('API_KEY' in config['Baidu_API']): 43 | self.__apiKey = config['Baidu_API']['API_KEY'].strip() 44 | if (self.__apiKey == ''): 45 | msg = 'API_KEY字段是空的' 46 | else: 47 | msg = 0 48 | else: 49 | msg = 'API_KEY字段不存在' 50 | 51 | if ('SECRET_KEY' in config['Baidu_API']): 52 | self.__secretKey = config['Baidu_API']['SECRET_KEY'].strip() 53 | if (self.__secretKey == ''): 54 | msg = 'SECRET_KEY字段是空的' 55 | else: 56 | msg = 0 57 | else: 58 | msg = 'SECRET_KEY字段不存在' 59 | else: 60 | msg = '请按照要求配置好env.ini文件' 61 | return msg 62 | 63 | def __del__(self): 64 | print('百度语音合成类使命完成') 65 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/python3 2 | # -*- coding: UTF-8 -*- 3 | 4 | from tencentApiClass import TencentApi 5 | # 百度的尚未完成 6 | # from baiduApiClass import BaiduApi 7 | from srtClass import SrtClass 8 | from audioDubClass import AudioDub 9 | def init(): 10 | # 启动合成类 11 | synt = TencentApi() 12 | # 启动生成音频类 13 | dub = AudioDub() 14 | # 获得pydub空对象 15 | sound = dub.getEmpty() 16 | 17 | # 处理srt文件 18 | srt = SrtClass("./srt/What Is Sociology.srt") 19 | # 获得第一段前的空白时间 20 | time = srt.strArr[0].start - 0 21 | # 拿出最后一个,它们需要特殊处理 22 | sArr = srt.strArr[:-1] 23 | # 计数 24 | count = 1 25 | # 循环生成音频 26 | for i in range(len(sArr)): 27 | print(i + 1) 28 | # 生成空白的时间 29 | sound += dub.addLacuna(time) 30 | # 当前音频持续时间 31 | time = srt.strArr[i].end - srt.strArr[i].start 32 | # 获得音频流 33 | stream = synt.getStream(text=srt.strArr[i].text, keepTime=time) 34 | # 用于计算差值,添加空白 35 | tmpSound = dub.getSound(stream) 36 | # 查看两者时间值 37 | tmpTime = time - len(tmpSound) 38 | print(tmpTime) 39 | if(tmpTime < -300): 40 | # 裁剪将会少字,建议将十分长的话在srt文件中缩短 41 | # 重新修剪 42 | tmpSound = dub.speedSound(tmpSound, time) 43 | # 合并音频,因为之前的声音都是空白所有可以直接加 44 | sound = dub.mergeSound(sound, tmpSound) 45 | 46 | print('srt文件时间:' + str(time)) 47 | print('音频时间:' + str(len(tmpSound))) 48 | print('留白时间:' + str(time - len(tmpSound))) 49 | 50 | # 再次查看两者时间值 51 | tmpTime = time - len(tmpSound) 52 | # 可能出现最快速度也大于srt的时间,那么直接裁剪 53 | if (tmpTime < 0): 54 | sound = sound[:tmpTime] 55 | else: 56 | # 添加空白 57 | sound += dub.addLacuna(tmpTime) 58 | # 设置第一段与第二段之间的空白 59 | time = srt.strArr[i + 1].start - srt.strArr[i].end 60 | print('分段时间:' + str(time)) 61 | # 命名 62 | name = str(count) + '.wav' 63 | count = count + 1 64 | # 限制生成文件的数量 65 | if count > 3: 66 | count = 1 67 | # 防报错导出部分音频 68 | dub.exportAudio(sound, name) 69 | 70 | # # 获得音频流 71 | stream = synt.synthesis(text=srt.strArr[-1].text) 72 | # # 用于计算差值,添加空白 73 | sound = dub.mergeSound(sound, dub.getSound(stream)) 74 | print('声音导出完毕!') 75 | # 导出音频 76 | dub.exportAudio(sound) 77 | 78 | if __name__ == '__main__': 79 | # 开始行动 80 | init() 81 | -------------------------------------------------------------------------------- /audioDubClass.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: UTF-8 -*- 3 | 4 | from pydub import AudioSegment, effects 5 | import time 6 | 7 | 8 | # 封装音频处理类 9 | class AudioDub(object): 10 | def __init__(self): 11 | 12 | return 13 | 14 | # 添加空白 15 | # sound 音频 16 | # time 留空的时间 17 | def addLacuna(self, time): 18 | return AudioSegment.silent(duration=time) 19 | 20 | # 返回一个空的pydub对象 21 | def getEmpty(self): 22 | return AudioSegment.empty() 23 | 24 | # 获得一段音频 25 | def getSound(self, stream): 26 | return AudioSegment( 27 | # raw audio data (bytes) 二进制数据流 28 | data=stream, 29 | # 采样位数 2 byte (16 bit) samples 通常有8bit、16bit、24bit和32bit等几种 30 | sample_width=2, 31 | 32 | # 44.1 kHz frame rate 采样率 33 | frame_rate=16000, 34 | 35 | # 声道 stereo 1 单声道 36 | channels=1 37 | ) 38 | 39 | # 音频合并 40 | # frontSound 放在前面的音频 41 | # behindSound 放在后面的音频 42 | def mergeSound(self, frontSound, behindSound): 43 | # 为过渡的时间添加空白 44 | frontSound += AudioSegment.silent(duration=100) 45 | # crossfade 用于过渡的时间 46 | return frontSound.append(behindSound, crossfade=100) 47 | 48 | # 导出音频 49 | def exportAudio(self, sound, name='test.wav'): 50 | formtTime = time.strftime("%Y-%m-%d", time.localtime()) 51 | sound.export('./sound/' + formtTime + '_' + name, format="wav") 52 | 53 | # 语音加速 54 | def speedSound(self, sound, keepTime): 55 | # 上一个的长度 56 | beforeLen = 0 57 | left = 100 58 | right = 130 59 | beforeSound = 0 60 | curSound = 0 61 | toalCount = 0 62 | while left < right: 63 | toalCount = toalCount + 1 64 | middle = int((left + right) / 2) 65 | # 参数: 音频 倍数 块大小 过渡时间 66 | curSound = effects.speedup(sound, middle / 100, 300, 100) 67 | soundLen = len(curSound) 68 | if soundLen == keepTime: 69 | return curSound 70 | elif right - left == 1 and soundLen < keepTime and soundLen < beforeLen: 71 | return beforeSound 72 | elif soundLen < keepTime: 73 | right = middle + 1 74 | beforeSound = curSound 75 | beforeLen = soundLen 76 | elif soundLen > keepTime: 77 | left = middle + 1 78 | beforeSound = curSound 79 | beforeLen = soundLen 80 | if toalCount == 30: 81 | break 82 | return curSound 83 | -------------------------------------------------------------------------------- /baidu_API/aip/speech.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Speech 5 | """ 6 | 7 | from .base import AipBase 8 | from .base import base64 9 | from .base import hashlib 10 | from .base import json 11 | 12 | class AipSpeech(AipBase): 13 | """ 14 | Aip Speech 15 | """ 16 | 17 | __asrUrl = 'http://vop.baidu.com/server_api' 18 | 19 | __ttsUrl = 'http://tsn.baidu.com/text2audio' 20 | 21 | def _isPermission(self, authObj): 22 | """ 23 | check whether permission 24 | """ 25 | 26 | return True 27 | 28 | def _proccessRequest(self, url, params, data, headers): 29 | """ 30 | 参数处理 31 | """ 32 | 33 | token = params.get('access_token', '') 34 | 35 | if not data.get('cuid', ''): 36 | data['cuid'] = hashlib.md5(token.encode()).hexdigest() 37 | 38 | if url == self.__asrUrl: 39 | data['token'] = token 40 | data = json.dumps(data) 41 | else: 42 | data['tok'] = token 43 | 44 | if 'access_token' in params: 45 | del params['access_token'] 46 | 47 | return data 48 | 49 | def _proccessResult(self, content): 50 | """ 51 | formate result 52 | """ 53 | 54 | try: 55 | return super(AipSpeech, self)._proccessResult(content) 56 | except Exception as e: 57 | return { 58 | '__json_decode_error': content, 59 | } 60 | 61 | def asr(self, speech=None, format='pcm', rate=16000, options=None): 62 | """ 63 | 语音识别 64 | """ 65 | 66 | data = {} 67 | 68 | if speech: 69 | data['speech'] = base64.b64encode(speech).decode() 70 | data['len'] = len(speech) 71 | 72 | data['channel'] = 1 73 | data['format'] = format 74 | data['rate'] = rate 75 | 76 | data = dict(data, **(options or {})) 77 | 78 | return self._request(self.__asrUrl, data) 79 | 80 | def synthesis(self, text, lang='zh', ctp=1, options=None): 81 | """ 82 | 语音合成 83 | """ 84 | data ={} 85 | 86 | data['tex'] = text 87 | data['lan'] = lang 88 | data['ctp'] = ctp 89 | 90 | data = dict(data, **(options or {})) 91 | 92 | result = self._request(self.__ttsUrl, data) 93 | 94 | if '__json_decode_error' in result: 95 | return result['__json_decode_error'] 96 | 97 | return result 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /baidu_API/aip/kg.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | 知识图谱 6 | """ 7 | 8 | import re 9 | import sys 10 | import math 11 | import time 12 | from .base import AipBase 13 | from .base import base64 14 | from .base import json 15 | from .base import urlencode 16 | from .base import quote 17 | 18 | class AipKg(AipBase): 19 | 20 | """ 21 | 知识图谱 22 | """ 23 | 24 | __createTaskUrl = 'https://aip.baidubce.com/rest/2.0/kg/v1/pie/task_create' 25 | 26 | __updateTaskUrl = 'https://aip.baidubce.com/rest/2.0/kg/v1/pie/task_update' 27 | 28 | __taskInfoUrl = 'https://aip.baidubce.com/rest/2.0/kg/v1/pie/task_info' 29 | 30 | __taskQueryUrl = 'https://aip.baidubce.com/rest/2.0/kg/v1/pie/task_query' 31 | 32 | __taskStartUrl = 'https://aip.baidubce.com/rest/2.0/kg/v1/pie/task_start' 33 | 34 | __taskStatusUrl = 'https://aip.baidubce.com/rest/2.0/kg/v1/pie/task_status' 35 | 36 | 37 | def createTask(self, name, template_content, input_mapping_file, output_file, url_pattern, options=None): 38 | """ 39 | 创建任务 40 | """ 41 | options = options or {} 42 | 43 | data = {} 44 | data['name'] = name 45 | data['template_content'] = template_content 46 | data['input_mapping_file'] = input_mapping_file 47 | data['output_file'] = output_file 48 | data['url_pattern'] = url_pattern 49 | 50 | data.update(options) 51 | 52 | return self._request(self.__createTaskUrl, data) 53 | 54 | def updateTask(self, id, options=None): 55 | """ 56 | 更新任务 57 | """ 58 | options = options or {} 59 | 60 | data = {} 61 | data['id'] = id 62 | 63 | data.update(options) 64 | 65 | return self._request(self.__updateTaskUrl, data) 66 | 67 | def getTaskInfo(self, id, options=None): 68 | """ 69 | 获取任务详情 70 | """ 71 | options = options or {} 72 | 73 | data = {} 74 | data['id'] = id 75 | 76 | data.update(options) 77 | 78 | return self._request(self.__taskInfoUrl, data) 79 | 80 | def getUserTasks(self, options=None): 81 | """ 82 | 以分页的方式查询当前用户所有的任务信息 83 | """ 84 | options = options or {} 85 | 86 | data = {} 87 | 88 | data.update(options) 89 | 90 | return self._request(self.__taskQueryUrl, data) 91 | 92 | def startTask(self, id, options=None): 93 | """ 94 | 启动任务 95 | """ 96 | options = options or {} 97 | 98 | data = {} 99 | data['id'] = id 100 | 101 | data.update(options) 102 | 103 | return self._request(self.__taskStartUrl, data) 104 | 105 | def getTaskStatus(self, id, options=None): 106 | """ 107 | 查询任务状态 108 | """ 109 | options = options or {} 110 | 111 | data = {} 112 | data['id'] = id 113 | 114 | data.update(options) 115 | 116 | return self._request(self.__taskStatusUrl, data) 117 | -------------------------------------------------------------------------------- /baidu_API/aip/imagecensor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import re 4 | import sys 5 | from .base import AipBase 6 | from .base import base64 7 | from .base import json 8 | from .base import urlencode 9 | from .base import quote 10 | 11 | class AipImageCensor(AipBase): 12 | """ 13 | Aip ImageCensor 14 | """ 15 | 16 | __antiPornUrl = 'https://aip.baidubce.com/rest/2.0/antiporn/v1/detect' 17 | 18 | __antiPornGifUrl = 'https://aip.baidubce.com/rest/2.0/antiporn/v1/detect_gif' 19 | 20 | __antiTerrorUrl = 'https://aip.baidubce.com/rest/2.0/antiterror/v1/detect' 21 | 22 | __faceAuditUrl = 'https://aip.baidubce.com/rest/2.0/solution/v1/face_audit' 23 | 24 | __imageCensorCombUrl = 'https://aip.baidubce.com/api/v1/solution/direct/img_censor' 25 | 26 | __imageCensorUserDefinedUrl = 'https://aip.baidubce.com/rest/2.0/solution/v1/img_censor/user_defined' 27 | 28 | def antiPorn(self, image): 29 | """ 30 | antiporn 31 | """ 32 | 33 | data = {} 34 | data['image'] = base64.b64encode(image).decode() 35 | 36 | return self._request(self.__antiPornUrl, data) 37 | 38 | def antiPornGif(self, image): 39 | """ 40 | antiporn gif 41 | """ 42 | 43 | data = {} 44 | data['image'] = base64.b64encode(image).decode() 45 | 46 | return self._request(self.__antiPornGifUrl, data) 47 | 48 | def antiTerror(self, image): 49 | """ 50 | antiterror 51 | """ 52 | 53 | data = {} 54 | data['image'] = base64.b64encode(image).decode() 55 | 56 | return self._request(self.__antiTerrorUrl, data) 57 | 58 | def faceAudit(self, images, configId=''): 59 | """ 60 | faceAudit 61 | """ 62 | 63 | # 非数组则处理为数组 64 | if not isinstance(images, list): 65 | images = [images] 66 | 67 | data = { 68 | 'configId': configId 69 | } 70 | 71 | isUrl = images[0][0:4] == 'http' 72 | if not isUrl: 73 | data['images'] = ','.join([ 74 | base64.b64encode(image).decode() for image in images 75 | ]) 76 | else: 77 | data['imgUrls'] = ','.join([ 78 | quote(url) for url in images 79 | ]) 80 | 81 | return self._request(self.__faceAuditUrl, data) 82 | 83 | def imageCensorComb(self, image, scenes='antiporn', options=None): 84 | """ 85 | imageCensorComb 86 | """ 87 | 88 | options = options or {} 89 | 90 | if not isinstance(scenes, list): 91 | scenes = scenes.split(',') 92 | 93 | data = { 94 | 'scenes': scenes, 95 | } 96 | 97 | isUrl = image.strip()[0:4] == 'http' 98 | if not isUrl: 99 | data['image'] = base64.b64encode(image).decode() 100 | else: 101 | data['imgUrl'] = image 102 | 103 | data.update(options) 104 | 105 | return self._request(self.__imageCensorCombUrl, json.dumps(data), { 106 | 'Content-Type': 'application/json', 107 | }) 108 | 109 | def imageCensorUserDefined(self, image): 110 | """ 111 | imageCensorUserDefined 112 | """ 113 | 114 | data = {} 115 | 116 | isUrl = image[0:4] == 'http' 117 | if not isUrl: 118 | data['image'] = base64.b64encode(image).decode() 119 | else: 120 | data['imgUrl'] = image 121 | 122 | return self._request(self.__imageCensorUserDefinedUrl, data) 123 | -------------------------------------------------------------------------------- /baidu_API/aip/imageclassify.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | 图像识别 6 | """ 7 | 8 | import re 9 | import sys 10 | import math 11 | import time 12 | from .base import AipBase 13 | from .base import base64 14 | from .base import json 15 | from .base import urlencode 16 | from .base import quote 17 | 18 | class AipImageClassify(AipBase): 19 | 20 | """ 21 | 图像识别 22 | """ 23 | 24 | __dishDetectUrl = 'https://aip.baidubce.com/rest/2.0/image-classify/v2/dish' 25 | 26 | __carDetectUrl = 'https://aip.baidubce.com/rest/2.0/image-classify/v1/car' 27 | 28 | __logoSearchUrl = 'https://aip.baidubce.com/rest/2.0/image-classify/v2/logo' 29 | 30 | __logoAddUrl = 'https://aip.baidubce.com/rest/2.0/realtime_search/v1/logo/add' 31 | 32 | __logoDeleteUrl = 'https://aip.baidubce.com/rest/2.0/realtime_search/v1/logo/delete' 33 | 34 | __animalDetectUrl = 'https://aip.baidubce.com/rest/2.0/image-classify/v1/animal' 35 | 36 | __plantDetectUrl = 'https://aip.baidubce.com/rest/2.0/image-classify/v1/plant' 37 | 38 | __objectDetectUrl = 'https://aip.baidubce.com/rest/2.0/image-classify/v1/object_detect' 39 | 40 | 41 | def dishDetect(self, image, options=None): 42 | """ 43 | 菜品识别 44 | """ 45 | options = options or {} 46 | 47 | data = {} 48 | data['image'] = base64.b64encode(image).decode() 49 | 50 | data.update(options) 51 | 52 | return self._request(self.__dishDetectUrl, data) 53 | 54 | def carDetect(self, image, options=None): 55 | """ 56 | 车辆识别 57 | """ 58 | options = options or {} 59 | 60 | data = {} 61 | data['image'] = base64.b64encode(image).decode() 62 | 63 | data.update(options) 64 | 65 | return self._request(self.__carDetectUrl, data) 66 | 67 | def logoSearch(self, image, options=None): 68 | """ 69 | logo商标识别 70 | """ 71 | options = options or {} 72 | 73 | data = {} 74 | data['image'] = base64.b64encode(image).decode() 75 | 76 | data.update(options) 77 | 78 | return self._request(self.__logoSearchUrl, data) 79 | 80 | def logoAdd(self, image, brief, options=None): 81 | """ 82 | logo商标识别—添加 83 | """ 84 | options = options or {} 85 | 86 | data = {} 87 | data['image'] = base64.b64encode(image).decode() 88 | data['brief'] = brief 89 | 90 | data.update(options) 91 | 92 | return self._request(self.__logoAddUrl, data) 93 | 94 | def logoDeleteByImage(self, image, options=None): 95 | """ 96 | logo商标识别—删除 97 | """ 98 | options = options or {} 99 | 100 | data = {} 101 | data['image'] = base64.b64encode(image).decode() 102 | 103 | data.update(options) 104 | 105 | return self._request(self.__logoDeleteUrl, data) 106 | 107 | def logoDeleteBySign(self, cont_sign, options=None): 108 | """ 109 | logo商标识别—删除 110 | """ 111 | options = options or {} 112 | 113 | data = {} 114 | data['cont_sign'] = cont_sign 115 | 116 | data.update(options) 117 | 118 | return self._request(self.__logoDeleteUrl, data) 119 | 120 | def animalDetect(self, image, options=None): 121 | """ 122 | 动物识别 123 | """ 124 | options = options or {} 125 | 126 | data = {} 127 | data['image'] = base64.b64encode(image).decode() 128 | 129 | data.update(options) 130 | 131 | return self._request(self.__animalDetectUrl, data) 132 | 133 | def plantDetect(self, image, options=None): 134 | """ 135 | 植物识别 136 | """ 137 | options = options or {} 138 | 139 | data = {} 140 | data['image'] = base64.b64encode(image).decode() 141 | 142 | data.update(options) 143 | 144 | return self._request(self.__plantDetectUrl, data) 145 | 146 | def objectDetect(self, image, options=None): 147 | """ 148 | 图像主体检测 149 | """ 150 | options = options or {} 151 | 152 | data = {} 153 | data['image'] = base64.b64encode(image).decode() 154 | 155 | data.update(options) 156 | 157 | return self._request(self.__objectDetectUrl, data) 158 | -------------------------------------------------------------------------------- /baidu_API/aip/nlp.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | 自然语言处理 6 | """ 7 | 8 | import re 9 | import sys 10 | import math 11 | import time 12 | from .base import AipBase 13 | from .base import base64 14 | from .base import json 15 | from .base import urlencode 16 | from .base import quote 17 | 18 | class AipNlp(AipBase): 19 | 20 | """ 21 | 自然语言处理 22 | """ 23 | 24 | __lexerUrl = 'https://aip.baidubce.com/rpc/2.0/nlp/v1/lexer' 25 | 26 | __lexerCustomUrl = 'https://aip.baidubce.com/rpc/2.0/nlp/v1/lexer_custom' 27 | 28 | __depParserUrl = 'https://aip.baidubce.com/rpc/2.0/nlp/v1/depparser' 29 | 30 | __wordEmbeddingUrl = 'https://aip.baidubce.com/rpc/2.0/nlp/v2/word_emb_vec' 31 | 32 | __dnnlmCnUrl = 'https://aip.baidubce.com/rpc/2.0/nlp/v2/dnnlm_cn' 33 | 34 | __wordSimEmbeddingUrl = 'https://aip.baidubce.com/rpc/2.0/nlp/v2/word_emb_sim' 35 | 36 | __simnetUrl = 'https://aip.baidubce.com/rpc/2.0/nlp/v2/simnet' 37 | 38 | __commentTagUrl = 'https://aip.baidubce.com/rpc/2.0/nlp/v2/comment_tag' 39 | 40 | __sentimentClassifyUrl = 'https://aip.baidubce.com/rpc/2.0/nlp/v1/sentiment_classify' 41 | 42 | def _proccessResult(self, content): 43 | """ 44 | formate result 45 | """ 46 | 47 | if sys.version_info.major == 2: 48 | return json.loads(content.decode('gbk', 'ignore').encode('utf8')) or {} 49 | else: 50 | return json.loads(str(content, 'gbk')) or {} 51 | 52 | def _proccessRequest(self, url, params, data, headers): 53 | """ 54 | _proccessRequest 55 | """ 56 | 57 | if sys.version_info.major == 2: 58 | return json.dumps(data, ensure_ascii=False).decode('utf8').encode('gbk') 59 | else: 60 | return json.dumps(data, ensure_ascii=False).encode('gbk') 61 | 62 | def lexer(self, text, options=None): 63 | """ 64 | 词法分析 65 | """ 66 | options = options or {} 67 | 68 | data = {} 69 | data['text'] = text 70 | 71 | data.update(options) 72 | 73 | return self._request(self.__lexerUrl, data) 74 | 75 | def lexerCustom(self, text, options=None): 76 | """ 77 | 词法分析(定制版) 78 | """ 79 | options = options or {} 80 | 81 | data = {} 82 | data['text'] = text 83 | 84 | data.update(options) 85 | 86 | return self._request(self.__lexerCustomUrl, data) 87 | 88 | def depParser(self, text, options=None): 89 | """ 90 | 依存句法分析 91 | """ 92 | options = options or {} 93 | 94 | data = {} 95 | data['text'] = text 96 | 97 | data.update(options) 98 | 99 | return self._request(self.__depParserUrl, data) 100 | 101 | def wordEmbedding(self, word, options=None): 102 | """ 103 | 词向量表示 104 | """ 105 | options = options or {} 106 | 107 | data = {} 108 | data['word'] = word 109 | 110 | data.update(options) 111 | 112 | return self._request(self.__wordEmbeddingUrl, data) 113 | 114 | def dnnlm(self, text, options=None): 115 | """ 116 | DNN语言模型 117 | """ 118 | options = options or {} 119 | 120 | data = {} 121 | data['text'] = text 122 | 123 | data.update(options) 124 | 125 | return self._request(self.__dnnlmCnUrl, data) 126 | 127 | def wordSimEmbedding(self, word_1, word_2, options=None): 128 | """ 129 | 词义相似度 130 | """ 131 | options = options or {} 132 | 133 | data = {} 134 | data['word_1'] = word_1 135 | data['word_2'] = word_2 136 | 137 | data.update(options) 138 | 139 | return self._request(self.__wordSimEmbeddingUrl, data) 140 | 141 | def simnet(self, text_1, text_2, options=None): 142 | """ 143 | 短文本相似度 144 | """ 145 | options = options or {} 146 | 147 | data = {} 148 | data['text_1'] = text_1 149 | data['text_2'] = text_2 150 | 151 | data.update(options) 152 | 153 | return self._request(self.__simnetUrl, data) 154 | 155 | def commentTag(self, text, options=None): 156 | """ 157 | 评论观点抽取 158 | """ 159 | options = options or {} 160 | 161 | data = {} 162 | data['text'] = text 163 | 164 | data.update(options) 165 | 166 | return self._request(self.__commentTagUrl, data) 167 | 168 | def sentimentClassify(self, text, options=None): 169 | """ 170 | 情感倾向分析 171 | """ 172 | options = options or {} 173 | 174 | data = {} 175 | data['text'] = text 176 | 177 | data.update(options) 178 | 179 | return self._request(self.__sentimentClassifyUrl, data) 180 | -------------------------------------------------------------------------------- /baidu_API/aip/imagesearch.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | 图像搜索 6 | """ 7 | 8 | import re 9 | import sys 10 | import math 11 | import time 12 | from .base import AipBase 13 | from .base import base64 14 | from .base import json 15 | from .base import urlencode 16 | from .base import quote 17 | 18 | class AipImageSearch(AipBase): 19 | 20 | """ 21 | 图像搜索 22 | """ 23 | 24 | __sameHqAddUrl = 'https://aip.baidubce.com/rest/2.0/realtime_search/same_hq/add' 25 | 26 | __sameHqSearchUrl = 'https://aip.baidubce.com/rest/2.0/realtime_search/same_hq/search' 27 | 28 | __sameHqDeleteUrl = 'https://aip.baidubce.com/rest/2.0/realtime_search/same_hq/delete' 29 | 30 | __similarAddUrl = 'https://aip.baidubce.com/rest/2.0/image-classify/v1/realtime_search/similar/add' 31 | 32 | __similarSearchUrl = 'https://aip.baidubce.com/rest/2.0/image-classify/v1/realtime_search/similar/search' 33 | 34 | __similarDeleteUrl = 'https://aip.baidubce.com/rest/2.0/image-classify/v1/realtime_search/similar/delete' 35 | 36 | __productAddUrl = 'https://aip.baidubce.com/rest/2.0/image-classify/v1/realtime_search/product/add' 37 | 38 | __productSearchUrl = 'https://aip.baidubce.com/rest/2.0/image-classify/v1/realtime_search/product/search' 39 | 40 | __productDeleteUrl = 'https://aip.baidubce.com/rest/2.0/image-classify/v1/realtime_search/product/delete' 41 | 42 | 43 | def sameHqAdd(self, image, options=None): 44 | """ 45 | 相同图检索—入库 46 | """ 47 | options = options or {} 48 | 49 | data = {} 50 | data['image'] = base64.b64encode(image).decode() 51 | 52 | data.update(options) 53 | 54 | return self._request(self.__sameHqAddUrl, data) 55 | 56 | def sameHqSearch(self, image, options=None): 57 | """ 58 | 相同图检索—检索 59 | """ 60 | options = options or {} 61 | 62 | data = {} 63 | data['image'] = base64.b64encode(image).decode() 64 | 65 | data.update(options) 66 | 67 | return self._request(self.__sameHqSearchUrl, data) 68 | 69 | def sameHqDeleteByImage(self, image, options=None): 70 | """ 71 | 相同图检索—删除 72 | """ 73 | options = options or {} 74 | 75 | data = {} 76 | data['image'] = base64.b64encode(image).decode() 77 | 78 | data.update(options) 79 | 80 | return self._request(self.__sameHqDeleteUrl, data) 81 | 82 | def sameHqDeleteBySign(self, cont_sign, options=None): 83 | """ 84 | 相同图检索—删除 85 | """ 86 | options = options or {} 87 | 88 | data = {} 89 | data['cont_sign'] = cont_sign 90 | 91 | data.update(options) 92 | 93 | return self._request(self.__sameHqDeleteUrl, data) 94 | 95 | def similarAdd(self, image, options=None): 96 | """ 97 | 相似图检索—入库 98 | """ 99 | options = options or {} 100 | 101 | data = {} 102 | data['image'] = base64.b64encode(image).decode() 103 | 104 | data.update(options) 105 | 106 | return self._request(self.__similarAddUrl, data) 107 | 108 | def similarSearch(self, image, options=None): 109 | """ 110 | 相似图检索—检索 111 | """ 112 | options = options or {} 113 | 114 | data = {} 115 | data['image'] = base64.b64encode(image).decode() 116 | 117 | data.update(options) 118 | 119 | return self._request(self.__similarSearchUrl, data) 120 | 121 | def similarDeleteByImage(self, image, options=None): 122 | """ 123 | 相似图检索—删除 124 | """ 125 | options = options or {} 126 | 127 | data = {} 128 | data['image'] = base64.b64encode(image).decode() 129 | 130 | data.update(options) 131 | 132 | return self._request(self.__similarDeleteUrl, data) 133 | 134 | def similarDeleteBySign(self, cont_sign, options=None): 135 | """ 136 | 相似图检索—删除 137 | """ 138 | options = options or {} 139 | 140 | data = {} 141 | data['cont_sign'] = cont_sign 142 | 143 | data.update(options) 144 | 145 | return self._request(self.__similarDeleteUrl, data) 146 | 147 | def productAdd(self, image, options=None): 148 | """ 149 | 商品检索—入库 150 | """ 151 | options = options or {} 152 | 153 | data = {} 154 | data['image'] = base64.b64encode(image).decode() 155 | 156 | data.update(options) 157 | 158 | return self._request(self.__productAddUrl, data) 159 | 160 | def productSearch(self, image, options=None): 161 | """ 162 | 商品检索—检索 163 | """ 164 | options = options or {} 165 | 166 | data = {} 167 | data['image'] = base64.b64encode(image).decode() 168 | 169 | data.update(options) 170 | 171 | return self._request(self.__productSearchUrl, data) 172 | 173 | def productDeleteByImage(self, image, options=None): 174 | """ 175 | 商品检索—删除 176 | """ 177 | options = options or {} 178 | 179 | data = {} 180 | data['image'] = base64.b64encode(image).decode() 181 | 182 | data.update(options) 183 | 184 | return self._request(self.__productDeleteUrl, data) 185 | 186 | def productDeleteBySign(self, cont_sign, options=None): 187 | """ 188 | 商品检索—删除 189 | """ 190 | options = options or {} 191 | 192 | data = {} 193 | data['cont_sign'] = cont_sign 194 | 195 | data.update(options) 196 | 197 | return self._request(self.__productDeleteUrl, data) 198 | 199 | -------------------------------------------------------------------------------- /tencentApiClass.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: UTF-8 -*- 3 | import configparser 4 | # 导入腾讯的pythonSDK GitHub:https://github.com/TencentCloud/tencentcloud-sdk-python 5 | from tencentcloud.common import credential 6 | from tencentcloud.common.profile.client_profile import ClientProfile 7 | from tencentcloud.common.profile.http_profile import HttpProfile 8 | from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException 9 | from tencentcloud.tts.v20190823 import tts_client, models 10 | import uuid 11 | import base64 12 | from pydub import AudioSegment 13 | import time 14 | # 封装腾讯语音合成接口类 15 | class TencentApi(object): 16 | def __init__(self): 17 | msg = self.__setApiInfo() 18 | if (msg != 0): 19 | print(msg) 20 | 21 | # 设置并返回正确时间的音频流 这会导致重复请求多次 22 | # keepTime 音频持续时间 23 | def getStream(self, text, keepTime, voiceType='0'): 24 | # 上一个的长度 25 | beforeLen = 0 26 | # 上一个的流 27 | beforeStream = 0 28 | # 最慢 29 | left = -20 30 | # 最快 31 | right = 20 32 | # 音频流 33 | stream=0 34 | # 二分搜索 找到最靠近srt文件所指的长度,且小于srt文件所指的长度 这可能会导致一句话被多次请求 预计最多6次能够找到 35 | while left < right: 36 | # 设置中间值 37 | middle = int((left + right) / 2) 38 | # 获取音频流 39 | stream = self.synthesis(text, voiceType, str(middle / 10)) 40 | # 当前流的长度 41 | curLen = self.audioLen(stream) 42 | # 当前流对应所需时间,直接返回 43 | if curLen == keepTime: 44 | return stream 45 | # 找到最小值 46 | elif right - left == 1 and curLen < keepTime and curLen < beforeLen: 47 | return beforeStream 48 | elif curLen < keepTime: 49 | beforeLen = curLen 50 | beforeStream = stream 51 | right = middle - 1 52 | elif curLen > keepTime: 53 | beforeLen = curLen 54 | beforeStream = stream 55 | left = middle + 1 56 | time.sleep(0.5) 57 | # 这表明最快速度也大于srt的时间 58 | return stream 59 | 60 | # 返回音频的长度 61 | # stream 音频流 62 | def audioLen(self, stream): 63 | sound = AudioSegment( 64 | # raw audio data (bytes) 二进制数据流 65 | data=stream, 66 | # 采样位数 2 byte (16 bit) samples 通常有8bit、16bit、24bit和32bit等几种 67 | sample_width=2, 68 | 69 | # 44.1 kHz frame rate 采样率 70 | frame_rate=16000, 71 | 72 | # 声道 stereo 1 单声道 73 | channels=1 74 | ) 75 | return len(sound) 76 | 77 | # 腾讯tts请求的参数设置 78 | # Text 需要转换成语音的文本 中文最大支持100个汉字(全角标点符号算一个汉字) 79 | # VoiceType 声音的类型 包括 80 | # 0-云小宁,亲和女声(默认) 81 | # 1-云小奇,亲和男声 82 | # 2-云小晚,成熟男声 83 | # 4-云小叶,温暖女声 84 | # 5-云小欣,情感女声 85 | # 6-云小龙,情感男声 86 | # 1000-智侠、情感男声(新) 87 | # 1001-智瑜,情感女声(新) 88 | # 1002-智聆,通用女声(新) 89 | # 1003-智美,客服女声(新) 90 | # 1050-WeJack,英文男声(新) 91 | # 1051-WeRose,英文女声(新) 92 | # speend 用于控制播放速度 93 | def synthesis(self, text, voiceType='0', speed='0'): 94 | try: 95 | # 获取腾讯云凭证 96 | cred = credential.Credential(self.__secretId, self.__secretKey) 97 | # 请求的配置 98 | httpProfile = HttpProfile() 99 | # 请求访问的域名 100 | httpProfile.endpoint = "tts.tencentcloudapi.com" 101 | # sdk的配置 102 | clientProfile = ClientProfile() 103 | # 设置sdk的请求配置 104 | clientProfile.httpProfile = httpProfile 105 | # 腾讯tts句柄 106 | # "ap-guangzhou" 该值可能为请求的服务器位置,这里为广州,可登录腾讯云控制台查看:https://console.cloud.tencent.com/api/explorer?Product=tts 107 | client = tts_client.TtsClient(cred, "ap-guangzhou", clientProfile) 108 | # 腾讯tts请求参数结构体 109 | req = models.TextToVoiceRequest() 110 | params = '{"Text":"' + text + '","VoiceType":"' + voiceType + '","SessionId":"' + str( 111 | uuid.uuid4()) + '","ModelType":1,"Speed":' + speed + ',"SampleRate":16000,"Codec":"wav","Volume":0}' 112 | req.from_json_string(params) 113 | # 生成base64语音 114 | resp = client.TextToVoice(req) 115 | # 将base64的字符转换成二进制流 116 | return base64.b64decode(resp.Audio) 117 | except TencentCloudSDKException as err: 118 | print(err) 119 | 120 | # env.ini信息 121 | 122 | def __setApiInfo(self): 123 | msg = 0 124 | # 读取配置文件 125 | config = configparser.ConfigParser() 126 | if len(config.read("./env.ini")) == 0: 127 | msg = '没有配置文件' 128 | else: 129 | # 以下信息需要百度智能云生成应用时获得 130 | if 'Tencent_API' in config: 131 | if ('AppId' in config['Tencent_API']): 132 | self.__appId = config['Tencent_API']['AppId'].strip() 133 | if (self.__appId == ''): 134 | msg = 'AppId字段是空的' 135 | else: 136 | msg = 0 137 | else: 138 | msg = 'AppId字段不存在' 139 | 140 | if ('SecretId' in config['Tencent_API']): 141 | self.__secretId = config['Tencent_API']['SecretId'].strip() 142 | if (self.__secretId == ''): 143 | msg = 'SecretId字段是空的' 144 | else: 145 | msg = 0 146 | else: 147 | msg = 'SecretId字段不存在' 148 | 149 | if ('SecretKey' in config['Tencent_API']): 150 | self.__secretKey = config['Tencent_API']['SecretKey'].strip() 151 | if (self.__secretKey == ''): 152 | msg = 'SecretKey字段是空的' 153 | else: 154 | msg = 0 155 | else: 156 | msg = 'SecretKey字段不存在' 157 | else: 158 | msg = '请按照要求配置好env.ini文件' 159 | return msg 160 | 161 | def __del__(self): 162 | print('腾讯语音合成类使命完成') 163 | -------------------------------------------------------------------------------- /baidu_API/aip/face.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | 人脸识别 6 | """ 7 | 8 | import re 9 | import sys 10 | import math 11 | import time 12 | from .base import AipBase 13 | from .base import base64 14 | from .base import json 15 | from .base import urlencode 16 | from .base import quote 17 | 18 | class AipFace(AipBase): 19 | 20 | """ 21 | 人脸识别 22 | """ 23 | 24 | __detectUrl = 'https://aip.baidubce.com/rest/2.0/face/v2/detect' 25 | 26 | __matchUrl = 'https://aip.baidubce.com/rest/2.0/face/v2/match' 27 | 28 | __identifyUrl = 'https://aip.baidubce.com/rest/2.0/face/v2/identify' 29 | 30 | __verifyUrl = 'https://aip.baidubce.com/rest/2.0/face/v2/verify' 31 | 32 | __userAddUrl = 'https://aip.baidubce.com/rest/2.0/face/v2/faceset/user/add' 33 | 34 | __userUpdateUrl = 'https://aip.baidubce.com/rest/2.0/face/v2/faceset/user/update' 35 | 36 | __userDeleteUrl = 'https://aip.baidubce.com/rest/2.0/face/v2/faceset/user/delete' 37 | 38 | __userGetUrl = 'https://aip.baidubce.com/rest/2.0/face/v2/faceset/user/get' 39 | 40 | __groupGetlistUrl = 'https://aip.baidubce.com/rest/2.0/face/v2/faceset/group/getlist' 41 | 42 | __groupGetusersUrl = 'https://aip.baidubce.com/rest/2.0/face/v2/faceset/group/getusers' 43 | 44 | __groupAdduserUrl = 'https://aip.baidubce.com/rest/2.0/face/v2/faceset/group/adduser' 45 | 46 | __groupDeleteuserUrl = 'https://aip.baidubce.com/rest/2.0/face/v2/faceset/group/deleteuser' 47 | 48 | 49 | def detect(self, image, options=None): 50 | """ 51 | 人脸检测 52 | """ 53 | options = options or {} 54 | 55 | data = {} 56 | data['image'] = base64.b64encode(image).decode() 57 | 58 | data.update(options) 59 | 60 | return self._request(self.__detectUrl, data) 61 | 62 | def match(self, images, options=None): 63 | """ 64 | 人脸比对 65 | """ 66 | options = options or {} 67 | 68 | data = {} 69 | data['images'] = ','.join([ 70 | base64.b64encode(image).decode() for image in images 71 | ]) 72 | 73 | data.update(options) 74 | 75 | return self._request(self.__matchUrl, data) 76 | 77 | def identifyUser(self, group_id, image, options=None): 78 | """ 79 | 人脸识别 80 | """ 81 | options = options or {} 82 | 83 | data = {} 84 | data['group_id'] = group_id 85 | data['image'] = base64.b64encode(image).decode() 86 | 87 | data.update(options) 88 | 89 | return self._request(self.__identifyUrl, data) 90 | 91 | def verifyUser(self, uid, group_id, image, options=None): 92 | """ 93 | 人脸认证 94 | """ 95 | options = options or {} 96 | 97 | data = {} 98 | data['uid'] = uid 99 | data['group_id'] = group_id 100 | data['image'] = base64.b64encode(image).decode() 101 | 102 | data.update(options) 103 | 104 | return self._request(self.__verifyUrl, data) 105 | 106 | def addUser(self, uid, user_info, group_id, image, options=None): 107 | """ 108 | 人脸注册 109 | """ 110 | options = options or {} 111 | 112 | data = {} 113 | data['uid'] = uid 114 | data['user_info'] = user_info 115 | data['group_id'] = group_id 116 | data['image'] = base64.b64encode(image).decode() 117 | 118 | data.update(options) 119 | 120 | return self._request(self.__userAddUrl, data) 121 | 122 | def updateUser(self, uid, user_info, group_id, image, options=None): 123 | """ 124 | 人脸更新 125 | """ 126 | options = options or {} 127 | 128 | data = {} 129 | data['uid'] = uid 130 | data['user_info'] = user_info 131 | data['group_id'] = group_id 132 | data['image'] = base64.b64encode(image).decode() 133 | 134 | data.update(options) 135 | 136 | return self._request(self.__userUpdateUrl, data) 137 | 138 | def deleteUser(self, uid, options=None): 139 | """ 140 | 人脸删除 141 | """ 142 | options = options or {} 143 | 144 | data = {} 145 | data['uid'] = uid 146 | 147 | data.update(options) 148 | 149 | return self._request(self.__userDeleteUrl, data) 150 | 151 | def getUser(self, uid, options=None): 152 | """ 153 | 用户信息查询 154 | """ 155 | options = options or {} 156 | 157 | data = {} 158 | data['uid'] = uid 159 | 160 | data.update(options) 161 | 162 | return self._request(self.__userGetUrl, data) 163 | 164 | def getGroupList(self, options=None): 165 | """ 166 | 组列表查询 167 | """ 168 | options = options or {} 169 | 170 | data = {} 171 | 172 | data.update(options) 173 | 174 | return self._request(self.__groupGetlistUrl, data) 175 | 176 | def getGroupUsers(self, group_id, options=None): 177 | """ 178 | 组内用户列表查询 179 | """ 180 | options = options or {} 181 | 182 | data = {} 183 | data['group_id'] = group_id 184 | 185 | data.update(options) 186 | 187 | return self._request(self.__groupGetusersUrl, data) 188 | 189 | def addGroupUser(self, src_group_id, group_id, uid, options=None): 190 | """ 191 | 组间复制用户 192 | """ 193 | options = options or {} 194 | 195 | data = {} 196 | data['src_group_id'] = src_group_id 197 | data['group_id'] = group_id 198 | data['uid'] = uid 199 | 200 | data.update(options) 201 | 202 | return self._request(self.__groupAdduserUrl, data) 203 | 204 | def deleteGroupUser(self, group_id, uid, options=None): 205 | """ 206 | 组内删除用户 207 | """ 208 | options = options or {} 209 | 210 | data = {} 211 | data['group_id'] = group_id 212 | data['uid'] = uid 213 | 214 | data.update(options) 215 | 216 | return self._request(self.__groupDeleteuserUrl, data) 217 | 218 | -------------------------------------------------------------------------------- /baidu_API/aip/base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | AipBase 5 | """ 6 | import hmac 7 | import json 8 | import hashlib 9 | import datetime 10 | import base64 11 | import time 12 | import sys 13 | import requests 14 | requests.packages.urllib3.disable_warnings() 15 | 16 | 17 | if sys.version_info.major == 2: 18 | from urllib import urlencode 19 | from urllib import quote 20 | from urlparse import urlparse 21 | else: 22 | from urllib.parse import urlencode 23 | from urllib.parse import quote 24 | from urllib.parse import urlparse 25 | 26 | class AipBase(object): 27 | """ 28 | AipBase 29 | """ 30 | 31 | __accessTokenUrl = 'https://aip.baidubce.com/oauth/2.0/token' 32 | 33 | __reportUrl = 'https://aip.baidubce.com/rpc/2.0/feedback/v1/report' 34 | 35 | __scope = 'brain_all_scope' 36 | 37 | def __init__(self, appId, apiKey, secretKey): 38 | """ 39 | AipBase(appId, apiKey, secretKey) 40 | """ 41 | 42 | self._appId = appId.strip() 43 | self._apiKey = apiKey.strip() 44 | self._secretKey = secretKey.strip() 45 | self._authObj = {} 46 | self._isCloudUser = None 47 | self.__client = requests 48 | self.__connectTimeout = 60.0 49 | self.__socketTimeout = 60.0 50 | self._proxies = {} 51 | self.__version = '2_0_0' 52 | 53 | def getVersion(self): 54 | """ 55 | version 56 | """ 57 | return self.__version 58 | 59 | def setConnectionTimeoutInMillis(self, ms): 60 | """ 61 | setConnectionTimeoutInMillis 62 | """ 63 | 64 | self.__connectTimeout = ms / 1000.0 65 | 66 | def setSocketTimeoutInMillis(self, ms): 67 | """ 68 | setSocketTimeoutInMillis 69 | """ 70 | 71 | self.__socketTimeout = ms / 1000.0 72 | 73 | def setProxies(self, proxies): 74 | """ 75 | proxies 76 | """ 77 | 78 | self._proxies = proxies 79 | 80 | def _request(self, url, data, headers=None): 81 | """ 82 | self._request('', {}) 83 | """ 84 | try: 85 | result = self._validate(url, data) 86 | if result != True: 87 | return result 88 | 89 | authObj = self._auth() 90 | params = self._getParams(authObj) 91 | 92 | data = self._proccessRequest(url, params, data, headers) 93 | headers = self._getAuthHeaders('POST', url, params, headers) 94 | response = self.__client.post(url, data=data, params=params, 95 | headers=headers, verify=False, timeout=( 96 | self.__connectTimeout, 97 | self.__socketTimeout, 98 | ), proxies=self._proxies 99 | ) 100 | obj = self._proccessResult(response.content) 101 | 102 | if not self._isCloudUser and obj.get('error_code', '') == 110: 103 | authObj = self._auth(True) 104 | params = self._getParams(authObj) 105 | response = self.__client.post(url, data=data, params=params, 106 | headers=headers, verify=False, timeout=( 107 | self.__connectTimeout, 108 | self.__socketTimeout, 109 | ), proxies=self._proxies 110 | ) 111 | obj = self._proccessResult(response.content) 112 | except (requests.exceptions.ReadTimeout, requests.exceptions.ConnectTimeout) as e: 113 | return { 114 | 'error_code': 'SDK108', 115 | 'error_msg': 'connection or read data timeout', 116 | } 117 | 118 | return obj 119 | 120 | def _validate(self, url, data): 121 | """ 122 | validate 123 | """ 124 | 125 | return True 126 | 127 | def _proccessRequest(self, url, params, data, headers): 128 | """ 129 | 参数处理 130 | """ 131 | 132 | params['aipSdk'] = 'python' 133 | params['aipVersion'] = self.__version 134 | 135 | return data 136 | 137 | def _proccessResult(self, content): 138 | """ 139 | formate result 140 | """ 141 | 142 | if sys.version_info.major == 2: 143 | return json.loads(content) or {} 144 | else: 145 | return json.loads(content.decode()) or {} 146 | 147 | def _auth(self, refresh=False): 148 | """ 149 | api access auth 150 | """ 151 | 152 | #未过期 153 | if not refresh: 154 | tm = self._authObj.get('time', 0) + int(self._authObj.get('expires_in', 0)) - 30 155 | if tm > int(time.time()): 156 | return self._authObj 157 | 158 | obj = self.__client.get(self.__accessTokenUrl, verify=False, params={ 159 | 'grant_type': 'client_credentials', 160 | 'client_id': self._apiKey, 161 | 'client_secret': self._secretKey, 162 | }, timeout=( 163 | self.__connectTimeout, 164 | self.__socketTimeout, 165 | ), proxies=self._proxies).json() 166 | 167 | self._isCloudUser = not self._isPermission(obj) 168 | obj['time'] = int(time.time()) 169 | self._authObj = obj 170 | 171 | return obj 172 | 173 | def _isPermission(self, authObj): 174 | """ 175 | check whether permission 176 | """ 177 | 178 | scopes = authObj.get('scope', '') 179 | 180 | return self.__scope in scopes.split(' ') 181 | 182 | def _getParams(self, authObj): 183 | """ 184 | api request http url params 185 | """ 186 | 187 | params = {} 188 | 189 | if self._isCloudUser == False: 190 | params['access_token'] = authObj['access_token'] 191 | 192 | return params 193 | 194 | def _getAuthHeaders(self, method, url, params=None, headers=None): 195 | """ 196 | api request http headers 197 | """ 198 | 199 | headers = headers or {} 200 | params = params or {} 201 | 202 | if self._isCloudUser == False: 203 | return headers 204 | 205 | urlResult = urlparse(url) 206 | for kv in urlResult.query.strip().split('&'): 207 | if kv: 208 | k, v = kv.split('=') 209 | params[k] = v 210 | 211 | # UTC timestamp 212 | timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') 213 | headers['Host'] = urlResult.hostname 214 | headers['x-bce-date'] = timestamp 215 | version, expire = '1', '1800' 216 | 217 | # 1 Generate SigningKey 218 | val = "bce-auth-v%s/%s/%s/%s" % (version, self._apiKey, timestamp, expire) 219 | signingKey = hmac.new(self._secretKey.encode('utf-8'), val.encode('utf-8'), 220 | hashlib.sha256 221 | ).hexdigest() 222 | 223 | # 2 Generate CanonicalRequest 224 | # 2.1 Genrate CanonicalURI 225 | canonicalUri = quote(urlResult.path) 226 | # 2.2 Generate CanonicalURI: not used here 227 | # 2.3 Generate CanonicalHeaders: only include host here 228 | 229 | canonicalHeaders = [] 230 | for header, val in headers.items(): 231 | canonicalHeaders.append( 232 | '%s:%s' % ( 233 | quote(header.strip(), '').lower(), 234 | quote(val.strip(), '') 235 | ) 236 | ) 237 | canonicalHeaders = '\n'.join(sorted(canonicalHeaders)) 238 | 239 | # 2.4 Generate CanonicalRequest 240 | canonicalRequest = '%s\n%s\n%s\n%s' % ( 241 | method.upper(), 242 | canonicalUri, 243 | '&'.join(sorted(urlencode(params).split('&'))), 244 | canonicalHeaders 245 | ) 246 | 247 | # 3 Generate Final Signature 248 | signature = hmac.new(signingKey.encode('utf-8'), canonicalRequest.encode('utf-8'), 249 | hashlib.sha256 250 | ).hexdigest() 251 | 252 | headers['authorization'] = 'bce-auth-v%s/%s/%s/%s/%s/%s' % ( 253 | version, 254 | self._apiKey, 255 | timestamp, 256 | expire, 257 | ';'.join(headers.keys()).lower(), 258 | signature 259 | ) 260 | 261 | return headers 262 | 263 | def report(self, feedback): 264 | """ 265 | 数据反馈 266 | """ 267 | 268 | data = {} 269 | data['feedback'] = feedback 270 | 271 | return self._request(self.__reportUrl, data) 272 | -------------------------------------------------------------------------------- /baidu_API/aip/ocr.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | 图像识别 6 | """ 7 | 8 | import re 9 | import sys 10 | import math 11 | import time 12 | from .base import AipBase 13 | from .base import base64 14 | from .base import json 15 | from .base import urlencode 16 | from .base import quote 17 | 18 | class AipOcr(AipBase): 19 | 20 | """ 21 | 图像识别 22 | """ 23 | 24 | __generalBasicUrl = 'https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic' 25 | 26 | __accurateBasicUrl = 'https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic' 27 | 28 | __generalUrl = 'https://aip.baidubce.com/rest/2.0/ocr/v1/general' 29 | 30 | __accurateUrl = 'https://aip.baidubce.com/rest/2.0/ocr/v1/accurate' 31 | 32 | __generalEnhancedUrl = 'https://aip.baidubce.com/rest/2.0/ocr/v1/general_enhanced' 33 | 34 | __webImageUrl = 'https://aip.baidubce.com/rest/2.0/ocr/v1/webimage' 35 | 36 | __idcardUrl = 'https://aip.baidubce.com/rest/2.0/ocr/v1/idcard' 37 | 38 | __bankcardUrl = 'https://aip.baidubce.com/rest/2.0/ocr/v1/bankcard' 39 | 40 | __drivingLicenseUrl = 'https://aip.baidubce.com/rest/2.0/ocr/v1/driving_license' 41 | 42 | __vehicleLicenseUrl = 'https://aip.baidubce.com/rest/2.0/ocr/v1/vehicle_license' 43 | 44 | __licensePlateUrl = 'https://aip.baidubce.com/rest/2.0/ocr/v1/license_plate' 45 | 46 | __businessLicenseUrl = 'https://aip.baidubce.com/rest/2.0/ocr/v1/business_license' 47 | 48 | __receiptUrl = 'https://aip.baidubce.com/rest/2.0/ocr/v1/receipt' 49 | 50 | __tableRecognizeUrl = 'https://aip.baidubce.com/rest/2.0/solution/v1/form_ocr/request' 51 | 52 | __tableResultGetUrl = 'https://aip.baidubce.com/rest/2.0/solution/v1/form_ocr/get_request_result' 53 | 54 | 55 | def basicGeneral(self, image, options=None): 56 | """ 57 | 通用文字识别 58 | """ 59 | options = options or {} 60 | 61 | data = {} 62 | data['image'] = base64.b64encode(image).decode() 63 | 64 | data.update(options) 65 | 66 | return self._request(self.__generalBasicUrl, data) 67 | 68 | def basicGeneralUrl(self, url, options=None): 69 | """ 70 | 通用文字识别 71 | """ 72 | options = options or {} 73 | 74 | data = {} 75 | data['url'] = url 76 | 77 | data.update(options) 78 | 79 | return self._request(self.__generalBasicUrl, data) 80 | 81 | def basicAccurate(self, image, options=None): 82 | """ 83 | 通用文字识别(高精度版) 84 | """ 85 | options = options or {} 86 | 87 | data = {} 88 | data['image'] = base64.b64encode(image).decode() 89 | 90 | data.update(options) 91 | 92 | return self._request(self.__accurateBasicUrl, data) 93 | 94 | def general(self, image, options=None): 95 | """ 96 | 通用文字识别(含位置信息版) 97 | """ 98 | options = options or {} 99 | 100 | data = {} 101 | data['image'] = base64.b64encode(image).decode() 102 | 103 | data.update(options) 104 | 105 | return self._request(self.__generalUrl, data) 106 | 107 | def generalUrl(self, url, options=None): 108 | """ 109 | 通用文字识别(含位置信息版) 110 | """ 111 | options = options or {} 112 | 113 | data = {} 114 | data['url'] = url 115 | 116 | data.update(options) 117 | 118 | return self._request(self.__generalUrl, data) 119 | 120 | def accurate(self, image, options=None): 121 | """ 122 | 通用文字识别(含位置高精度版) 123 | """ 124 | options = options or {} 125 | 126 | data = {} 127 | data['image'] = base64.b64encode(image).decode() 128 | 129 | data.update(options) 130 | 131 | return self._request(self.__accurateUrl, data) 132 | 133 | def enhancedGeneral(self, image, options=None): 134 | """ 135 | 通用文字识别(含生僻字版) 136 | """ 137 | options = options or {} 138 | 139 | data = {} 140 | data['image'] = base64.b64encode(image).decode() 141 | 142 | data.update(options) 143 | 144 | return self._request(self.__generalEnhancedUrl, data) 145 | 146 | def enhancedGeneralUrl(self, url, options=None): 147 | """ 148 | 通用文字识别(含生僻字版) 149 | """ 150 | options = options or {} 151 | 152 | data = {} 153 | data['url'] = url 154 | 155 | data.update(options) 156 | 157 | return self._request(self.__generalEnhancedUrl, data) 158 | 159 | def webImage(self, image, options=None): 160 | """ 161 | 网络图片文字识别 162 | """ 163 | options = options or {} 164 | 165 | data = {} 166 | data['image'] = base64.b64encode(image).decode() 167 | 168 | data.update(options) 169 | 170 | return self._request(self.__webImageUrl, data) 171 | 172 | def webImageUrl(self, url, options=None): 173 | """ 174 | 网络图片文字识别 175 | """ 176 | options = options or {} 177 | 178 | data = {} 179 | data['url'] = url 180 | 181 | data.update(options) 182 | 183 | return self._request(self.__webImageUrl, data) 184 | 185 | def idcard(self, image, id_card_side, options=None): 186 | """ 187 | 身份证识别 188 | """ 189 | options = options or {} 190 | 191 | data = {} 192 | data['image'] = base64.b64encode(image).decode() 193 | data['id_card_side'] = id_card_side 194 | 195 | data.update(options) 196 | 197 | return self._request(self.__idcardUrl, data) 198 | 199 | def bankcard(self, image, options=None): 200 | """ 201 | 银行卡识别 202 | """ 203 | options = options or {} 204 | 205 | data = {} 206 | data['image'] = base64.b64encode(image).decode() 207 | 208 | data.update(options) 209 | 210 | return self._request(self.__bankcardUrl, data) 211 | 212 | def drivingLicense(self, image, options=None): 213 | """ 214 | 驾驶证识别 215 | """ 216 | options = options or {} 217 | 218 | data = {} 219 | data['image'] = base64.b64encode(image).decode() 220 | 221 | data.update(options) 222 | 223 | return self._request(self.__drivingLicenseUrl, data) 224 | 225 | def vehicleLicense(self, image, options=None): 226 | """ 227 | 行驶证识别 228 | """ 229 | options = options or {} 230 | 231 | data = {} 232 | data['image'] = base64.b64encode(image).decode() 233 | 234 | data.update(options) 235 | 236 | return self._request(self.__vehicleLicenseUrl, data) 237 | 238 | def licensePlate(self, image, options=None): 239 | """ 240 | 车牌识别 241 | """ 242 | options = options or {} 243 | 244 | data = {} 245 | data['image'] = base64.b64encode(image).decode() 246 | 247 | data.update(options) 248 | 249 | return self._request(self.__licensePlateUrl, data) 250 | 251 | def businessLicense(self, image, options=None): 252 | """ 253 | 营业执照识别 254 | """ 255 | options = options or {} 256 | 257 | data = {} 258 | data['image'] = base64.b64encode(image).decode() 259 | 260 | data.update(options) 261 | 262 | return self._request(self.__businessLicenseUrl, data) 263 | 264 | def receipt(self, image, options=None): 265 | """ 266 | 通用票据识别 267 | """ 268 | options = options or {} 269 | 270 | data = {} 271 | data['image'] = base64.b64encode(image).decode() 272 | 273 | data.update(options) 274 | 275 | return self._request(self.__receiptUrl, data) 276 | 277 | def tableRecognitionAsync(self, image, options=None): 278 | """ 279 | 表格文字识别 280 | """ 281 | options = options or {} 282 | 283 | data = {} 284 | data['image'] = base64.b64encode(image).decode() 285 | 286 | data.update(options) 287 | 288 | return self._request(self.__tableRecognizeUrl, data) 289 | 290 | def getTableRecognitionResult(self, request_id, options=None): 291 | """ 292 | 表格识别结果 293 | """ 294 | options = options or {} 295 | 296 | data = {} 297 | data['request_id'] = request_id 298 | 299 | data.update(options) 300 | 301 | return self._request(self.__tableResultGetUrl, data) 302 | 303 | def tableRecognition(self, image, options=None, timeout=10000): 304 | """ 305 | tableRecognition 306 | """ 307 | 308 | result = self.tableRecognitionAsync(image) 309 | 310 | if 'error_code' in result: 311 | return result 312 | 313 | requestId = result['result'][0]['request_id'] 314 | for i in range(int(math.ceil(timeout / 1000.0))): 315 | result = self.getTableRecognitionResult(requestId, options) 316 | 317 | # 完成 318 | if int(result['result']['ret_code']) == 3: 319 | break 320 | time.sleep(1) 321 | 322 | return result -------------------------------------------------------------------------------- /baidu_API/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------