两种方法去除页眉页脚

  • 前言
  • 1.基于转换为图片后的页眉页脚高度定位识别切割
  • 2.基于OCR识别后的文本去除

前言

如何去掉PDF或者WORD的页眉或者页脚?
由于需求涉及文本比对,页眉页脚会影响比对准确率,当前试过两种可以有效去除页眉页脚的方法,供大家参考思路和方法。

1.基于转换为图片后的页眉页脚高度定位识别切割

核心思路:首先将文档每一页转为图片,基于opencv的方法将图片二值化(即能分开空白区域和黑色文字像素),将图片降维存储为“心电图”,即可理解为将图片从上往下横向扫描切割,出现黑丝文字的区域越密集,“心电图”波峰越大。之后即可找到第一次出现波动的高度和该波动第一次消失的高度,第一次波动和最后一次波动即可分别视为页眉页脚出现的位置。(基于认知:页眉页脚应该出现在文档的最高处和最底处)

步骤:
1.首先将文档转为图片,网上有很多方法,可自行搜索。
2.比如得到测试用例:

3.对图片进行处理,进行横向映射:
split_img.py:

# coding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
from PIL import Imageclass SplitImg():def __init__(self):self.width = 0self.height = 0self.img_position = []self.img_matrix = []def dimension_reduction(self, img_path):'''对图片进行垂直和水平映射:param img_path: 图片存储路径:return:'''img = cv2.imread(img_path, 0)_, self.img_matrix = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY)print(self.img_matrix)horizontal_sum = np.sum(self.img_matrix, axis=1)plt.plot(horizontal_sum,range(horizontal_sum.shape[0]))plt.gca().invert_yaxis()plt.show()bin = self.img_matrix.Tvertical_sum = np.sum(bin, axis=1)# plt.plot(range(vertical_sum.shape[0]),vertical_sum)# plt.gca().invert_yaxis()# plt.show()self.height = horizontal_sum.shape[0]self.width = vertical_sum.shape[0]self.img_position = [[0, 0], [self.width, 0], [self.width, self.height], [0, self.height]]def extra_space(self, dirc, apex_position, img_matrix, width_space):if dirc == '-':mapping_sum = np.sum(img_matrix, axis=1)else:mapping_sum = np.sum(img_matrix.T, axis=1)mapping_size = mapping_sum.shape[0]mapping_space = []split_result = {}start_index = 0pre_num = mapping_sum[0]stat_num = 0for i in range(1, mapping_size):if pre_num+1000 > mapping_sum[i] > pre_num-1000:stat_num += 1else:if stat_num > self.width * width_space and start_index != 0:# if stat_num > self.width * 0.1 and start_index != 0:mapping_space.append((start_index+i-1)//2)stat_num = 0start_index = ipre_num = mapping_sum[i]if dirc == '-':x0_y1 = apex_position[0]x1_y1 = apex_position[1]for i in range(len(mapping_space)):x0_y0 = x0_y1x1_y0 = x1_y1x1_y1 = [apex_position[1][0], apex_position[1][1]+mapping_space[i]]x0_y1 = [apex_position[0][0], apex_position[0][1]+mapping_space[i]]split_result[str(i+1)] = [x0_y0, x1_y0, x1_y1, x0_y1]split_result[str(i+2)] = [x0_y1, x1_y1, apex_position[2], apex_position[3]]else:x1_y0 = apex_position[0]x1_y1 = apex_position[3]for i in range(len(mapping_space)):x0_y0 = x1_y0x0_y1 = x1_y1x1_y0 = [apex_position[0][0]+mapping_space[i], apex_position[1][1]]x1_y1 = [apex_position[0][0]+mapping_space[i], apex_position[2][1]]split_result[str(i+1)] = [x0_y0, x1_y0, x1_y1, x0_y1]split_result[str(i+2)] = [x1_y0, apex_position[1], apex_position[2], x1_y1]return split_resultdef split_img(self, width_space): #返回按空白行切割的模块dirc = '-'hori_blocks = self.extra_space(dirc, self.img_position, self.img_matrix, width_space)return hori_blocksdef split_img_cp(self, width_space):dirc = '-'hori_blocks = self.extra_space(dirc, self.img_position, self.img_matrix, width_space)print(hori_blocks)dirc = '|'split_result = {}for k,v in hori_blocks.items():current_matrix = np.asarray(self.img_matrix)[v[0][1]:v[2][1],v[0][0]:v[1][0]]vert_blocks = self.extra_space(dirc, v, current_matrix, width_space)for k1,v1 in vert_blocks.items():split_result['.'.join([k,k1])] = v1return split_resultdef run(self, img_path, width_space=0.01):self.dimension_reduction(img_path)return self.split_img(width_space)

3.去掉图片中的第一簇和最后一簇,得到结果:

这里没有去掉页脚的原因,代码中添加了误删逻辑,如果页眉页脚高度超过了整个文档比例的14%,则不删掉。

remove_img_header_footer.py:

from split_img import *
from file_diff.log import logger
import osdef deleteDir(path):"""删除路径或者文件:return:"""try:if os.path.isdir(path):shutil.rmtree(path)if os.path.isfile(path):os.remove(path)except Exception as e:error = traceback.format_exc().split('\n')logger.error('错误:\n{}'.format('\n'.join(error)))def run_remove_header_and_footer(img_path_dir,width_space=0.01):file_list = []for root, dirs, files in os.walk(img_path_dir):file_list = [(os.path.join(root, file), width_space) for file in files]for img_path,ws in file_list:remove_header_and_footer(img_path,ws)# print(file_list)def judge_exist_header(img_path_dir,width_space=0.01):file_list = []for root, dirs, files in os.walk(img_path_dir):file_list = [(os.path.join(root, file), width_space) for file in files]if(len(file_list)==1): #文件转为图片后只有一张#利用word允许的页眉最大高度判断,如果在高度以内存在文字,则默认为存在页眉#自己创建一个word最大高度的页眉页脚,填充上文字,然后转成PDF转成图片作为测试用例#经过验证后不可行,页眉可以设置成无穷高度#所以当只有一页图片时,为了稳妥起见,不处理passelif(len(file_list)>1):header_coordinates = []footer_coordinates = []for img_path, ws in file_list:logger.info("start to judge_exist_header")si = SplitImg()result = si.run(img_path, width_space)# 打开一张图img = Image.open(img_path)# 图片尺寸img_size = img.sizeprint(img_size)w = img_size[0]  # 图片宽度h = img_size[1]  # 图片高度for i, row_block in enumerate(result.values()):if (i == 0):logger.info('收集页眉坐标')header_coordinates.append((row_block[3],row_block[2])) #页眉矩形模块的下端坐标,从左向右elif (i == len(result.values()) - 1):logger.info('收集页脚坐标')footer_coordinates.append((row_block[0], row_block[1])) #页脚矩形模块的上端坐标,从左向右print("页眉坐标:",header_coordinates)print("页脚坐标:",footer_coordinates)def judge_exist_footer():passdef remove_header_and_footer(img_path, width_space=0.01):logger.info("start to remove header and footer")try:si = SplitImg()result = si.run(img_path, width_space)img = Image.open(img_path)img_size = img.sizeprint(img_size)w = img_size[0]  # 图片宽度h = img_size[1]  # 图片高度main_body = []print("len(result.values()):",len(result.values()))for i, row_block in enumerate(result.values()):if (i == 0):logger.info('跳过页眉')if(row_block[3][1]/h>=0.14): #如果页眉高度超过14%,则不切以防误伤到正文print("检测高度超过14%,不切页眉")main_body.append(row_block[0])main_body.append(row_block[1])else:main_body.append(row_block[3])main_body.append(row_block[2])elif (i == len(result.values()) - 1):logger.info('跳过页脚')if ((h-row_block[0][1]) / h >= 0.14):  # 如果页脚高度超过14%,则不切以防误伤到正文print("检测高度超过14%,不切页脚")main_body.append(row_block[2])main_body.append(row_block[3])else:main_body.append(row_block[1])main_body.append(row_block[0])# print(row_block)print(main_body)x = main_body[0][0]y = main_body[0][1]w = main_body[1][0] - main_body[0][0]h = main_body[2][1] - main_body[1][1]print(x,y,w,h)if(w==0 or h==0):# img.save(img_path)deleteDir(img_path)else:region = img.crop((x, y, x + w, y + h))# region.save(img_path[:-4]+"_removed_hdr_ftr"+img_path[-4:])region.save(img_path)except Exception as e:logger.error(e)returnlogger.info("success remove header and footer")if __name__ == '__main__':remove_header_and_footer('img.png',width_space=0.01)

其中width_space=0.01是阈值,该值越小则越苛刻(切的厉害,稍微一点白缝可能就是边界),越大越包容(不切)。页面高度0.14也可以根据文档的情况自行设置调整。
另外log.py和本案例关系不大主要是调试用,大家自行删除或者替换为print()即可。

之后再通过OCR图片识别的方法自然就没有页眉页脚了,可以发现,该方法可能存在误切到正文的问题,并且可能需要根据文档页眉页脚本身的特点调整参数。

2.基于OCR识别后的文本去除

思路:首先将文档直接转为图片,图片再通过OCR识别转为文字,传入Dict格式的预处理结果,具体格式为:
{‘jpg name1’: ‘content1’, ‘jpg name2’: ‘content2’, …}
测试用例如下:

dict1 = {
‘001.jpg’: ‘这里是页眉\nahhfadvdajv\n\n1’,
‘002.jpg’: ‘这里是页眉\n啊甘好难过好\n但是不管你会给你\n2’,
‘003.jpg’: ‘这里是页眉\n北京公司\n技术同\n甲方:(委托人)\n乙方:(受托人)\n根据《中华人民共和国合同法》的规定,XXXX\n3’,
‘004.jpg’: ‘这里是页眉\n北京有限公司\nsdivndjsdvnjsd\n\n4’
}

然后按照以下步骤处理:
1.首先通过“\n”换行符切割每一张图片的文本
2.根据切割后的结果得到每一页的list,那么页眉可能在每一页的list[0]出现,页脚可能在每一页的list[-1]出现,基于这样的思想,再利用文本相似度+基于最高频出现的文本长度的容错投票筛选机制来判断

这里主要是利用了所有页面的全局信息,以判断页眉为例子,即比较所有页面的list[0],list[1]…,判断他们的相似性,如果相似度超过0.8(可调整)即认为页眉内容相同,但有时候页眉页脚就是单纯的数字(如第一页“1”,第二页“2”),这样相似度就不管用了,引入第二种判断机制:基于认知:每一页页眉/页脚的文本格式长度应该相同,那么对于一个文档而言,通过OCR识别之后的页眉页脚长度也应该相同或者相似,那么当大部分页面比如50%以上的页面(投票思想,阈值可设置)的list[0]的长度都为X时,即可能list[0]都是页眉(判断list[-1]都是页脚的思想同理)。

3.按照上述思想依次比较每一页的相同行list[idx],直到该行在每一页的相似度和长度都不满足阈值时即可退出(对应代码中的:if (avg_similar_score <= 0.5 and vote_ratio <= 0.5): break

remove_str_header_footer.py:

import difflib
from collections import Counter,defaultdictdef string_similar(s1, s2):return difflib.SequenceMatcher(None, s1, s2).quick_ratio()def process_header(dict,page_to_remove):print("识别页眉")avg_similar_score = 1vote_ratio = 1idx = 0while (avg_similar_score > 0.5 or vote_ratio > 0.5):header_list = []header_content_len = []for item in dict.items():str = item[1].split("\n")# print(str)header_list.append({"str": str[idx], "len": len(str[idx])})header_content_len.append(len(str[idx]))times = 0total_score = 0for i in range(0, len(header_list)):for j in range(i + 1, len(header_list)):times += 1score = string_similar(header_list[i]["str"], header_list[j]["str"])total_score += scoreavg_similar_score = total_score / times# 计算字符串相同长度出现最多次数的频率占比dic = Counter(header_content_len)dic = sorted(dic.items(), key=lambda item: item[1], reverse=True)vote_ratio = dic[0][1] / len(header_content_len)if (avg_similar_score <= 0.5 and vote_ratio <= 0.5):breakprint(header_list)print("avg_similar_score:", avg_similar_score)print("出现最多的相同长度为 {} 的字符串次数:{},在所有页面中占比:{}".format(dic[0][0], dic[0][1], vote_ratio))# 如果平均相似度>0.8,直接全部删除if (avg_similar_score >= 0.8):for i,item in enumerate(dict.items()):#遍历每一页# print(item[1])str = item[1].split("\n") #每一页按行分割page_to_remove[i].append(idx) #第i页添加需要去掉的list索引# 如果相似度太低,按照最多相同长度为中心的[-2,2]范围,符合这个范围的页眉页脚都删掉else:for i,item in enumerate(header_list):#遍历每一页if(item["len"]>=dic[0][0]-2 and item["len"]<=dic[0][0]+2):page_to_remove[i].append(idx)idx += 1def process_footer(dict,page_to_remove):print("\n识别页脚")avg_similar_score = 1vote_ratio = 1idx = -1while (avg_similar_score > 0.5 or vote_ratio > 0.5):header_list = []header_content_len = []for item in dict.items():# print(item[0],item[1])str = item[1].split("\n")# print(str)header_list.append({"str": str[idx], "len": len(str[idx])})header_content_len.append(len(str[idx]))times = 0total_score = 0for i in range(0, len(header_list)):for j in range(i + 1, len(header_list)):times += 1score = string_similar(header_list[i]["str"], header_list[j]["str"])total_score += scoreavg_similar_score = total_score / times# 计算字符串相同长度出现最多次数的频率占比dic = Counter(header_content_len)dic = sorted(dic.items(), key=lambda item: item[1], reverse=True)vote_ratio = dic[0][1] / len(header_content_len)if (avg_similar_score <= 0.5 and vote_ratio <= 0.5):breakprint(header_list)print("avg_similar_score:", avg_similar_score)print("出现最多的相同长度为 {} 的字符串次数:{},在所有页面中占比:{}".format(dic[0][0], dic[0][1], vote_ratio))# 如果平均相似度>0.8,直接全部删除if (avg_similar_score >= 0.8):for i, item in enumerate(dict.items()):  # 遍历每一页page_to_remove[i].append(idx)  # 第i页添加需要去掉的list索引# 如果相似度太低,按照最多相同长度为中心的[-2,2]范围,符合这个范围的页眉页脚都删掉else:for i, item in enumerate(header_list):  # 遍历每一页if (item["len"] >= dic[0][0] - 2 and item["len"] <= dic[0][0] + 2):page_to_remove[i].append(idx)idx -= 1def remove_run(dict):page_to_remove = defaultdict(list)process_header(dict, page_to_remove)process_footer(dict, page_to_remove)print("\n<key:每一页,value: 该页需要删除的段落索引list>:\n",page_to_remove)result = ""for i, item in enumerate(dict.items()):  # 遍历每一页str = item[1].split("\n")  # 每一页按行分割print(str)delete_idx_in_page_i = page_to_remove[i]for idx,s in enumerate(str):if(idx in delete_idx_in_page_i or idx-len(str) in delete_idx_in_page_i):continueelse:result += s+"\n"return result[:-1] #最后多写入了一个换行符,去掉if __name__ == '__main__':dict1 = {'001.jpg': '这里是页眉\nahhfadvdajv\n\n1','002.jpg': '这里是页眉\n啊甘好难过好\n但是不管你会给你\n2','003.jpg': '这里是页眉\n北京公司\n技术同\n甲方:(委托人)\n乙方:(受托人)\n根据《中华人民共和国合同法》的规定,XXXX\n3','004.jpg': '这里是页眉\n北京有限公司\nsdivndjsdvnjsd\n\n4'}result = remove_run(dict1)print(result)

码字不易,期待您的关注!后台回复【关键词】免费领取本人上传至CSDN的所有资料~


两种方法去除页眉页脚:基于OCR识别后的文本/基于图片切割相关推荐

  1. 在Vue-cli脚手架中引入图片最常用的两种方法

    CommonJS API定义了很多普通应用程序(主要指非浏览器的应用)使用的API,require就说其中之一,我们通常需要在组件中引入图片时,可以在Data里使用这个方法require(" ...

  2. html+轮播图下标跳转代码,最简单的JavaScript图片轮播代码(两种方法)

    通过改变每个图片的opacity属性: 素材图片: 代码一: 最简单的轮播广告 body, div, ul, li { margin: ; padding: ; } ul { list-style-t ...

  3. html元素隐藏js 控制,JS控制HTML元素的显示和隐藏的两种方法

    JS控制HTML元素的显示和隐藏的两种方法 利用来JS控制页面控件显示和隐藏有两种方法,两种方法分别利用HTML的style中的两个属性,两种方法的不同之处在于控件隐藏后是否还在页面上占空位. 方法一 ...

  4. Jquery调用打印机打印(包含去除页眉页脚)

    个人小说网站友书-绿色.纯净.无广告欢迎广大同行前来指点.阅读 话不多说,直接上代码, function Print(data) {$.post("../../program/ashx/Pr ...

  5. js打印去除页眉页脚url地址

    js打印去除页眉页脚.url地址 调用window.print()方法打印时,有自带的页眉页脚和url地址不太美观,用以下方法即可去除页眉页脚和url 解决方案: // 在css中加入以下代码即可 @ ...

  6. 【Word】页眉页脚的设置方法

    最近正在准备毕业论文,看着学校统一的.复杂的,所谓"论文规范"直发呆,突然发觉,原来word也是有应用技术的呃-         一番摸索,讲一个比较生疏的一个设置方法,以供分享学 ...

  7. 论文中页眉页脚的设置方法~【转】

    最近正在准备毕业论文,看着学校统一的.复杂的,所谓"论文规范"直发呆,突然发觉,原来word也是有应用技术的呃-         一番摸索,讲一个比较生疏的一个设置方法,以供分享学 ...

  8. 今天教大家PDF文件怎么批量去除页眉页脚

    如今我们所阅读的电子文档大多都是PDF文档,其实和很多的网友都向我诉说了一个问题,如果是要重新编辑,把PDF文件重新修改是一件很麻烦的事.今天来给大家介绍一下应该如何去除PDF文件的页眉页脚,这个办法 ...

  9. excel插入页码_Excel里毫不起眼的页眉页脚,居然有这3种高能用法!

    点击上方蓝字关注星标★不迷路 本文作者:小敏 本文编辑:小叮 一说到页眉页脚,你脑海中出现的,是不是这样一个画面? 规规矩矩的页眉页脚,比如,公司 logo 或者文件名称放在页眉位置,底部加个页码. ...

  10. dede列表页if判断输出html,首页、列表页调用文章body内容的两种方法

    随着源码的开放性,很多SEOER对页面的要求也越来越复杂多样性,很多时候,织梦系统的原有功能并不能满足seoer的页面布置要求,这就需要继续开发页面,做更多的功能调用. 今天徐金华SEO给大家讲的是关 ...

最新文章

  1. js php 正则差别,正则表达式(括号)、[中括号]、{大括号}的区别小结,正则表达式小结...
  2. FFT频谱泄露和加窗 (一)
  3. FreeBSD 6.0架设管理与应用-附录B 制作FreeBSD安装光盘
  4. final,finally和finalize的区别
  5. C. Commentator problem
  6. firefox 复制文本js代码。判断窗口是关闭还是刷新
  7. 编写 Servlet 2.3 Filter
  8. 使用DSX2-5000 CH测试时选择(+PoE)和(+All)后缀的含义
  9. Hystrix---SpringCloud
  10. web developer tips (29):在web应用项目里启用“编辑并继续”功能
  11. 2021-2025年中国冻融室行业市场供需与战略研究报告
  12. 机器学习- 吴恩达Andrew Ng 编程作业技巧 for Week5 Neural Networks Learning
  13. 《高质量程序设计指南》读书笔记
  14. Python的dataframe_image使用报错
  15. Java项目第24期springboot实现简单点餐系统
  16. redis之禁用保护模式以及修改监听IP
  17. 苹果safari浏览器怎么样(好不好用)
  18. 2020 年中科院计算所“计算未来”全国大学生暑期班 网络数据科学与技术重点实验室 机试试题
  19. WPf 带滚动条WrapPanel 自动换行 和控件右键菜单
  20. vue+echarts地图下钻(全国-省-市)

热门文章

  1. vue2 配置本地IP地址访问项目
  2. Redis集群-哨兵
  3. 【转】 Linkdrops:以太坊上发红包的开源标准
  4. solidworks一份草图多次拉伸与多次凸台
  5. 浅谈老妈的QQ号被盗之后
  6. Unity Metaverse(五)、Avatar数字人换装系统的实现方案
  7. 让计算机休眠的命令,电脑怎么取消休眠?关闭休眠命令是什么?
  8. 获两个千亿IPO投资人王刚:要逼自己在质上进取,不是在量上贪婪
  9. U盘插入,无法读取?6种解决方法
  10. CPU使用率过高应该如何处理