在CentOS中,默认情况下,自带python2.7版本环境,但是由于业务需求,将python版本升级到python3.0以上版本,在网上找了一个教程,顺利的将python3.9安装好了,但是随之而来又出现了一个问题,由于安装了python3以上版本,导致yum命令不能正常使用。运行yum命令会报以下错误信息:
[root@localhost vagrant]# yum update zip File "/usr/bin/yum", line 30 except KeyboardInterrupt, e: ^ SyntaxError: invalid syntax
一、 问题根源
yum需要用python2编译,如果安装的是python3以上并作为默认编译器的话,就会报这个错误。
二、解决方案
主要修改文件中的第一行,默认情况下,文件的第一行代码如下:
1、编辑 /usr/bin/yum 文件
2、编辑 /usr/libexec/urlgrabber-ext-down
Original: https://blog.51cto.com/YangPC/5483086
Author: CorwinPC
Title: 安装python3后使用yum命令报错?
相关阅读1
Title: 用Python写了一个图像文字识别OCR工具
人生苦短,快学Python!
在之前的文章里,我们多次尝试用Python实现文本OCR识别!
今天我们要搞一个升级版:直接写一个 图像文字识别OCR工具 !
引言
最近在技术交流群里聊到一个关于图像文字识别的需求,在工作、生活中常常会用到,比如 票据、漫画、扫描件、照片的文本提取 。
博主基于 PyQt + labelme + PaddleOCR 写了一个桌面端的OCR工具,用于快速实现图片中 文本区域自动检测+文本自动识别 。
识别效果如下图所示:
干货主要有:
① 200 多本 Python 电子书(和经典的书籍)应该有
② Python标准库资料(最全中文版)
③ 项目源码(四五十个有趣且可靠的练手项目及源码)
④ Python基础入门、爬虫、网络开发、大数据分析方面的视频(适合小白学习)
⑤ Python学习路线图(告别不入流的学习)
Python学习Q群101677771
▲OCR工具识别效果
所有框选区域为OCR算法自动检测,右侧列表有每个框对应的文字内容;点击右侧"识别结果"中的文本记录,然后点击"复制到剪贴板"即可复制该文本内容。
功能列表
- 文本区域检测+文字识别
- 文本区域可视化
- 文字内容列表
- 图像、文件夹加载
- 图像滚轮缩放查看
- 绘制区域、编辑区域
- 复制所选文本识别结果
OCR部分
图像文字检测+文字识别算法,主要借助 paddleocr 实现。
创建或者选择一个虚拟环境,安装需要用到的第三方库。
<span class="hljs-attr">conda <span class="hljs-string">create -n ocr
<span class="hljs-attr">conda <span class="hljs-string">activate ocr
</span></span></span></span>
安装框架
如果你没有NVIDIA GPU,或GPU不支持CUDA,可以安装CPU版本:
如果你的GPU安装过CUDA9或CUDA10,cuDNN 7.6+,可以选择下面这个GPU版本:
安装 PaddleOCR
安装paddleocr:
<span class="hljs-attribute">pip install <span class="hljs-string">"paddleocr>=2.0.1" </span></span>
版面分析,需要安装 Layout-Parser:
<span class="hljs-attribute">pip3 install -U https://paddleocr.bj.bcebos.com/whl/layoutparser-0.0.0-py3-none-any.whl
</span>
测试安装是否成功
安装完成后,测试一张图片 --image_dir ./imgs/11.jpg ,采用中英文检测+方向分类器+识别全流程:
<span class="hljs-string">paddleocr <span class="hljs-string">--image_dir <span class="hljs-string">./imgs/11.jpg <span class="hljs-string">--use_angle_cls <span class="hljs-literal">true <span class="hljs-string">--use_gpu <span class="hljs-literal">false
</span></span></span></span></span></span></span>
输出一个list:
在python中调用
from paddleocr <span class="hljs-keyword">import PaddleOCR, draw_ocr
# Paddleocr目前支持的多语言语种可以通过修改lang参数进行切换
# 例如<span class="hljs-string">`ch`, <span class="hljs-string">`en`, <span class="hljs-string">`fr`, <span class="hljs-string">`german`, <span class="hljs-string">`korean`, <span class="hljs-string">`japan`
ocr = PaddleOCR(use_angle_cls=True, lang=<span class="hljs-string">"ch") # need to run only once to download and load model into memory
img_path = <span class="hljs-string">'./imgs/11.jpg'
result = ocr.ocr(img_path, cls=True)
<span class="hljs-keyword">for line in result:
<span class="hljs-built_in">print(line)
</span></span></span></span></span></span></span></span></span></span></span>
输出结果是一个list,每个item包含了文本框,文字和识别置信度:
[[[24.0, 36.0], [304.0, 34.0], [304.0, 72.0], [24.0, 74.0]], ['纯臻营养护发素', 0.964739]] [[[24.0, 80.0], [172.0, 80.0], [172.0, 104.0], [24.0, 104.0]], ['产品信息/参数', 0.98069626]] [[[24.0, 109.0], [333.0, 109.0], [333.0, 136.0], [24.0, 136.0]], ['(45元/每公斤,100公斤起订)', 0.9676722]] ......
界面部分
界面部分基于pyqt5实现,其中pyqt GUI程序开发入门和环境配置,详见一篇博客(具体见文末)。
主要步骤:
界面布局设计
在QtDesigner中拖拽控件,完成程序界面布局,并保存 *.ui 文件。
利用 pyuic 自动生成界面代码
在 pycharm 的项目文件结构中找到 *.ui 文件,右键——External Tools——pyuic,会在ui文件同级目录下自动生成界面 ui 的 Python 代码。
编写界面业务类
业务类 MainWindow 实现程序逻辑和算法功能,与前面第2步生成的ui实现解耦,避免每次修改ui文件会影响业务代码。ui界面上的控件可以通过 self._ui.xxxObjectName 访问。
<span class="hljs-class"><span class="hljs-keyword">class <span class="hljs-title">MainWindow(<span class="hljs-title">QMainWindow):
FIT_WINDOW, FIT_WIDTH, MANUAL_ZOOM = <span class="hljs-number">0, <span class="hljs-number">1, <span class="hljs-number">2
<span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">__init__<span class="hljs-params">(<span class="hljs-keyword">self):
<span class="hljs-keyword">super().__init_<span class="hljs-number">_() </span></span></span></span></span></span></span></span></span></span></span></span></span></span>
实现界面业务逻辑
对主界面上的按钮、列表、绘图控件进行 信号槽连接 。自定义的槽函数不用专门声明,如果是自定义的信号,需要在类__init__()前加上 yourSignal= pyqtSignal(args) 。
这里以按钮响应函数、列表响应函数为例。按钮点击的信号是 clicked ,listWidget列表切换选择的信号是 itemSelectionChanged 。
# 按钮响应函数
<span class="hljs-selector-tag">self<span class="hljs-selector-class">._ui<span class="hljs-selector-class">.btnOpenImg<span class="hljs-selector-class">.clicked<span class="hljs-selector-class">.connect(<span class="hljs-selector-tag">self<span class="hljs-selector-class">.openFile)
<span class="hljs-selector-tag">self<span class="hljs-selector-class">._ui<span class="hljs-selector-class">.btnOpenDir<span class="hljs-selector-class">.clicked<span class="hljs-selector-class">.connect(<span class="hljs-selector-tag">self<span class="hljs-selector-class">.openDirDialog)
<span class="hljs-selector-tag">self<span class="hljs-selector-class">._ui<span class="hljs-selector-class">.btnNext<span class="hljs-selector-class">.clicked<span class="hljs-selector-class">.connect(<span class="hljs-selector-tag">self<span class="hljs-selector-class">.openNextImg)
<span class="hljs-selector-tag">self<span class="hljs-selector-class">._ui<span class="hljs-selector-class">.btnPrev<span class="hljs-selector-class">.clicked<span class="hljs-selector-class">.connect(<span class="hljs-selector-tag">self<span class="hljs-selector-class">.openPrevImg)
<span class="hljs-selector-tag">self<span class="hljs-selector-class">._ui<span class="hljs-selector-class">.btnStartProcess<span class="hljs-selector-class">.clicked<span class="hljs-selector-class">.connect(<span class="hljs-selector-tag">self<span class="hljs-selector-class">.startProcess)
<span class="hljs-selector-tag">self<span class="hljs-selector-class">._ui<span class="hljs-selector-class">.btnCopyAll<span class="hljs-selector-class">.clicked<span class="hljs-selector-class">.connect(<span class="hljs-selector-tag">self<span class="hljs-selector-class">.copyToClipboard)
<span class="hljs-selector-tag">self<span class="hljs-selector-class">._ui<span class="hljs-selector-class">.btnSaveAll<span class="hljs-selector-class">.clicked<span class="hljs-selector-class">.connect(<span class="hljs-selector-tag">self<span class="hljs-selector-class">.saveToFile)
<span class="hljs-selector-tag">self<span class="hljs-selector-class">._ui<span class="hljs-selector-class">.listWidgetResults<span class="hljs-selector-class">.itemSelectionChanged<span class="hljs-selector-class">.connect(<span class="hljs-selector-tag">self<span class="hljs-selector-class">.onItemResultClicked)
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
5. 运行看看效果
运行 python main.py 即可启动GUI程序。
打开图片→选择语言模型ch(中文)→选择文本检测+识别→点击开始,检测完的文本区域会自动画框,并在右侧识别结果——文本Tab页的列表中显示。
所有检测出文本的区域列表,在识别结果——区域Tab页:
软件代码
由于时间有限,软件细节功能还需进一步完善。代码已开源到 gitee 上,欢迎感兴趣的朋友提pull request,共同修改完善。
代码开源地址:
https://gitee.com/signal926/ocr-gui-demoOriginal: https://www.cnblogs.com/sn5200/p/15912354.html
Author: Python可乐的呀
Title: 用Python写了一个图像文字识别OCR工具
相关阅读2
Title: Python 快速排序法(转)
方法解读:
例:对初始序列:"6 1 2 7 9 3 4 5 10 8"采用快速排序法:
一、分别从初始序列"6 1 2 7 9 3 4 5 10 8"两端开始"探测"。
先 从右往左找一个 小于6的数,再从 左往右找一个大于 6的数,然后 交换他们。
这里可以用两个变量 i 和 j ,分别指向序列最左边和最右边。
我们为这两个变量起个好听的名字" 哨兵i"和" 哨兵j"。
刚开始的时候让 哨兵i指向序列的最左边(即 i=1),指向 数字6。
让 哨兵j指向序列的最右边(即 j=10),指向 数字8。
二、首先哨兵j 开始出动。
因为此处设置的基准数是最左边的数,所以需要让 哨兵j先出动,这一点非常重要(请自己想一想为什么)。
哨兵j一步一步地向左挪动(即 j--),直到找到一个小于6的数停下来。
接下来 哨兵i 再一步一步向右挪动(即 i++),直到找到一个数大于6的数停下来。
最后 哨兵j 停在了数字5面前, 哨兵i 停在了数字7面前。
现在交换 哨兵i 和 哨兵j 所指向的元素的值。
到此,第一次交换结束。
三、接下来开始 哨兵j 继续向左挪动(再友情提醒,每次必须是 哨兵j先出发)。
他发现了4(比基准数6要小,满足要求)之后停了下来。
哨兵i 也继续向右挪动的,他发现了9(比基准数6要大,满足要求)之后停了下来。
此时再次进行交换。
四、第二次交换结束,"探测"继续。
哨兵j继续向左挪动,他发现了3(比基准数6要小,满足要求)之后又停了下来。
哨兵i 继续向右移动,糟啦!此时 哨兵i 和 哨兵j 相遇了, 哨兵i 和 哨兵j 都走到3面前。
说明此时"探测"结束。
我们将基准数6和3进行交换。交换之后的序列如下。
五、 到此第一轮"探测"真正结束。
此时以基准数6为分界点,6左边的数都小于等于6,6右边的数都大于等于6。
回顾一下刚才的过程,其实 哨兵j 的使命就是要找小于基准数的数,而 哨兵i 的使命就是要找大于基准数的数,直到 i 和 j 碰头为止。
现在基准数6已经归位,它正好处在序列的第6位。
此时我们已经将原来的序列,以6为分界点拆分成了两个序列,左边的序列是"3 1 2 5 4",右边的序列是"9 7 10 8"。
接下来还需要分别处理这两个序列。因为6左边和右边的序列目前都还是很混乱的。
不过不要紧,我们已经掌握了方法,接下来只要模拟刚才的方法分别处理6左边和右边的序列即可。
六、现在先来处理6左边的序列现吧。
左边的序列是"3 1 2 5 4"。请将这个序列以3为基准数进行调整,使得3左边的数都小于等于3,3右边的数都大于等于3。好了开始动笔吧。
如果你模拟的没有错,调整完毕之后的序列的顺序应该是。
2 1 3 5 4
OK,现在3已经归位。
接下来需要处理3左边的序列"2 1"和右边的序列"5 4"。
对序列"2 1"以2为基准数进行调整,处理完毕之后的序列为"1 2",到此2已经归位。
序列"1"只有一个数,也不需要进行任何处理。至此我们对序列"2 1"已全部处理完毕,得到序列是"1 2"。
序列"5 4"的处理也仿照此方法,最后得到的序列如下。
1 2 3 4 5 6 9 7 10 8
七、对于序列"9 7 10 8"也模拟刚才的过程,直到不可拆分出新的子序列为止。
最终将会得到这样的序列,如下。
1 2 3 4 5 6 7 8 9 10
八、到此,排序完全结束。
细心的同学可能已经发现,快速排序的每一轮处理其实就是将这一轮的基准数归位,直到所有的数都归位为止,排序就结束了。
下面上个霸气的图来描述下整个算法的处理过程。
九、原始网址:https://blog.csdn.net/adusts/article/details/80882649
十、Python程序:
#快速排序法一:有小到大排序
def quickSort(arr,left,right):#arr:待排序的数列;left:数列开始的下标索引;right:数列结束的下标索引
if left > right:#如果开始索引大于结束索引
return
temp=arr[left];#取最左边的数为 基准数
i,j=left,right
while i!= j:#现从右边开始找
while arr[j]>=temp and i
将 基准数 归位
arr[left]=arr[i]
arr[i]=temp
quickSort(arr,left,i-1)#递归:继续处理左边的
quickSort(arr,i+1,right)#递归:继续处理右边的
测试
arr=[10,7,8,8,9,1,5]
n=len(arr)
quickSort(arr,0,n-1)
print("排序后的数组:")
for i in range(n):
print("%d" %arr[i])
#快速排序法二:由大到小排序
def quickSort(arr,left,right):#arr:待排序的数列;left:数列开始的下标索引;right:数列结束的下标索引
if left > right:#如果开始索引大于结束索引
return
temp=arr[left];#取最左边的数为 基准数
i,j=left,right
while i!= j:#现从右边开始找
while arr[j] and i
将 基准数 归位
arr[left]=arr[i]
arr[i]=temp
quickSort(arr,left,i-1)#递归:继续处理左边的
quickSort(arr,i+1,right)#递归:继续处理右边的
测试
arr=[10,7,8,8,9,1,5]
n=len(arr)
quickSort(arr,0,n-1)
print("排序后的数组:")
for i in range(n):
print("%d" %arr[i])
Original: https://www.cnblogs.com/xiangers/p/15433774.html
Author: xiangers
Title: Python 快速排序法(转)
相关阅读3
Title: 计算机网络--运输层
物理层,数据链路层,网络层共同解决了 主机与主机之间的通信问题
但实际上在计算机网络中进行 通信的真正实体是位于通信两端主机中的进程
- 运输层协议【端到端协议】 解决:如何为运行在不同主机上的应用进程提供直接的通信服务是运输层的任务 根据需求不同,运输层为应用层提供了两种不同的运输协议
- 面向连接的 TCP
- 无连接的 UDP
- 运输层端口号 不是物理层面的接口,而是运行在计算机的进程使用的 进程标识符 PID 在不同操作系统中,使用不同格式的进程标识符,想要顺利通信 就必须使用统一的方法对 TCP/IP 体系的应用进程进行标识 TCP/IP 体系的运输层使用端口号区分应用层的不同应用进程
- 端口号使用 16 比特表示,取值 0 ~ 65535
- 熟知端口号:0 ~ 1023,IANA 把这些端口号指派给 TCP/IP 体系中最重要的一些应用协议,例如:PTF-21/20、HTTP-80,DNS-53
- 登记端口号:1024 ~ 49151,为没有熟知端口号的应用程序使用,使用这类端口号必须在 IANA 按照规定手续登记,防止重复
- 短暂进程号:49152 ~ 65535,留给客户进程选择暂时使用。当服务器进程收到客户进程的报文时,就知道客户进程所使用的动态端口号。通信结束后,可以提供给其他客户进程以后使用
- 端口号只具有本地意义,在不同计算机中相同端口是没有联系的
- 常用协议的运输层熟知端口号
- (RIP-520、DNS-53、TFTP-69、SNMP-161、DHCP-67/68) -> UDP UDP 协议字段为 17
- (SMTP-25、FTP-20/21、BGP-179、HTTP-80、HTTPS-443) -> TCP TCP 协议字段为 6
TCP 与 UDP 区别
- 用户数据协议 UDP: User Datagram Protocol
- 传输控制协议 TCP: Transmisson Control Protocol
UDP TCP 无连接 有连接 支持单播、多播、广播 仅支持单播 面向应用报文 面向字节流 向上层提供无连接不可靠传输服务【适用于 IP 电话、视频会议等实时应用】 向上层提供面向连接的可靠传输服务【适用于要求可靠传输的应用,例如文件传输】 用户数据报首部仅 8 字节 报文首部最小 20 字节,最大 60 字节 不使用流量控制和拥塞控制 使用流量控制和拥塞控制
TCP
固定首部 【 20 字节 】
- 源端口和目的端口【 各占2个字节 】:端口是传输层和应用层的服务接口,传输层的复用和分用功能都有要通过端口才能实现。
- 序号【 4 】:首部中的序号字段值则是指的是本报文段所发送的数据的第一个字节的序号
- 确认号【 4 】:是期望收到对方下一个报文段的第一个数据字节的序号。
- 数据偏移【4】:它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远
- 保留【 6 】:保留为今后使用,应置为0
- 标识符【 6 】
- 紧急URG:占1位,当URG=1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快发送,而不要按原来的排队顺序来传送
- 确认ACK:占1位, 仅当ACK = 1时确认号字段才有效。TCP规定,在连接建立后所有的传送的报文段都必须把ACK置为1。
- 推送PSH:占1位,表示Push功能是否启用
- 复位RST:占1位,当RST=1时,表名TCP连接中出现了严重错误,必须释放连接,然后再重新建立传输连接
- 同步SYN:占1位,在连接建立时用来同步序号
- 终止FIN:占1位,用来释放一个连接
- 窗口【 2 】:窗口指的是发送本报文段的一方的接受窗口
- 紧急指针【 2 】:紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数
扩展首部 【 最大 40 字节 】
- 选项:长度可变,最长可达40字节,对 TCP 功能的额外扩充
- 填充:为了使整个首部长度是 4 字节的整数倍
限制发送方的发送速率,让接收方能够完整的接收数据
实现:使用TCP协议的 滑动窗口机制实现
- TCP 接收方利用自己的接收窗口的大小来限制发送方发送窗口的大小
- TCP发送方收到接收方的 零窗口通知后,应启动持续计数器。计数器超时会发送一个 *零窗口探测报文
处理和缓解网络拥塞问题,避免无意义的网络资源浪费
TCP 拥塞控制算法
- Tahoe 版
- 慢开始:指数级增长,达到 ssthresh 门限改为 拥塞避免
- 拥塞避免:线性增长,超时重传触发,ssthresh / 2, cwnd = 1
- Reno 版
- 快重传:不再以超时重传为基准,而是收到 3 个重复确认触发
- 快恢复:在 ssthresh 更新为 ssthresh / 2 基础上更新 cwnd 值为 ssthresh 直接进入拥塞避免阶段
超时重传时间 RTO 应该略大于 往返时间 RTT
RFC6298 计算超时重传时间 RTO
[RTO = RTT_S + 4 \cdot RTT_D ]
计算 加权平均往返时间 (RTT_S)
( RTT_{S1} = RTT_1 \ new RTT_S = (1 - \alpha) \cdot old RTT_S + \alpha \cdot new RTT_{test}, \qquad 0 \leqslant \alpha < 1 )
已经成为建议标准的 RFC6298 的推荐 (\alpha = 1 / 8 = 0.125)
计算 (RTT) 偏差的加权平均 (RTT_D)
( RTT_{D1} = RTT_1 \div 2 \ new RTT_D = (1 - \beta) \cdot old RTT_D + \beta \cdot \mid RTT_S - new RTT_{test} \mid, \qquad 0 \leqslant \beta < 1 )
已经成为建议标准的 RFC6298 的推荐 (\beta = 1 / 4 = 0.25)
TCP 可靠连接的实现
基于 以字节为但我的滑动窗口来实现可靠传输
TCP 运输连接有一下三个阶段
- 建立 TCP 连接
- 数据传输
- 释放 TCP 连接
TCP 建立连接所需要解决的问题
- 使 TCP 双方能够确认对方的存在
- 达成双方的一些基础参数的确认
- 使双方能够对运输实体资源进行分配
TCP 建立连接被称为"三次握手"
- 客户端发送 TCP 连接请求:SYN=1, seq=x
- 服务器发送针对 TCP 连接请求的确认:SYN=1, ACK=1, seq=y, ack=k+1
- 客户端发送对服务器的回复确认:ACK=1, seq=x+1, ack=y+1
注:SYN=1 的报文段不能携带数据
分析:第三次回复确认的必要性
这是为了防止已经失效的连接请求报文段突然又传送到TCP服务器,导致TCP服务器单方面进入已建立连接状态造成不必要的资源浪费
TCP 释放连接被称为"四次挥手"
- 客户端发送 TCP 连接释放:FIN=1, ACK=1, seq=u, ack=v
- 服务器回复确认:ACK=1, seq=v, ack=u+1
- 服务器发送最终连接释放报文:FIN=1, ACK=1, seq=w, ack=u+1
- 客户端回复确认完成释放:ACK=1, seq=u+1, ack=w+1
在客户端发送最终确认后,需要经过 2MSL (Maximum Segment Lifetime) 进入关闭状态,RFC793 建议为 2 分钟
分析: 2MSL 等待的必要性
防止第四次挥手丢失,服务器再次发送第三次挥手,出现客户端已关闭的错误
保活计时器:确保客户端出现故障,无法释放连接
当保活计时器到时后,TCP 服务器进程就会向 TCP 客户进程发送一个探测报文段,以后每隔 75 秒发送一次,当连续累计 10 次无响应,则认为客户端故障
Original: https://www.cnblogs.com/shadow-/p/16262703.html
Author: shadow_D
Title: 计算机网络--运输层