当前位置: 首页 > news >正文

用python将一个扫描pdf文件改成二值图片组成的pdf文件

使用墨水屏读书现在似乎越来越流行,这确实有一定的好处,例如基本不发热,电池续航时间超长,基本不能游戏所以有利于沉浸式阅读,还有不知道是不是真的有用的所谓防蓝光伤害。但是,如果阅读的书籍是扫描图片组成的pdf,如果扫描的时候用的彩色模式,那么这种书籍在墨水屏上有点灰蒙蒙的,如果转换为256级灰度图片时最高灰度值太低,更加难以看清,这时候就可以考虑将这个pdf文件转换成二值图片(即每个像素不是白色就是纯黑的黑色)组成的pdf,这样效果就很好了。

先看看在PC上两种不同pdf文件的效果对比:

转换后的二值图片pdf效果:

转换前的效果:

尽管在非黑白墨水屏的设备上彩色pdf文件读起来更舒适,但是在黑白墨水屏上却刚好相反。下面的python程序就可以实现上述效果的转换(程序注释中标明的库的版本是本人测试环境中的版本,并非必须。其他版本可能也能够成功运行):

###############################################################
# 将彩色或灰度扫描pdf文件转换为二值的黑白pdf文件,在墨水屏上阅读时更为清晰 #
###############################################################import fitz # pip install pymupdf==1.24.14
import numpy as np # pip install numpy==2.1.1
from PIL import Image # pip install pillow==10.4.0file = 'test.pdf'
pdf_pages = fitz.open(file)
img_list = []
# 二值化阈值,可根据实际情况调整
threshold = 200
try:for page in pdf_pages:# 获取页面的图片数据,类型为pymupdf.Pixmappixmap = page.get_pixmap()# 解码为 np.uint8类型的numpy.ndarrayimage_array = np.frombuffer(pixmap.samples, dtype=np.uint8).reshape(pixmap.height, pixmap.width, pixmap.n)# 转换为PIL.Image.Image,通过三行代码将pymupdf.Pixmap转换成了PIL.Image.Imageimage = Image.fromarray(image_array)# 将彩色图片转换为黑白图片image = image.convert('L')# 获取图片的像素数据pixels = image.load()# 获取图片的宽度和高度width, height = image.size# 遍历每个像素点进行二值化处理for y in range(height):for x in range(width):# 获取当前像素的灰度值gray_value = pixels[x, y]# 小于阈值的像素点改成黑色,大于阈值的像素点改成白色if gray_value < threshold:pixels[x, y] = 0else:pixels[x, y] = 255# 将转换的二值图片加入列表img_list.append(image)# 将图片列表合并为一个pdf文件,resolution取值越大,pdf文件页面就可以放大更多倍数而不出现锯齿img_list[0].save(f'test_{threshold}.pdf','PDF', resolution=100.0,save_all=True, append_images=img_list[1:])
except Exception as e:print(e)
pdf_pages.close()

从本文图1看以上程序转换所得的页面效果还是有较大瑕疵,主要体现在有些文字笔画残缺,有些笔画互相粘黏,看起来挤成一团。如果在转换成二值图片前现对原图片进行自适应对比度增强,虽然会导致图像中增加一些噪点,但是整体阅读效果更好,如下图:

上图与图1比较文字显然更清晰,虽然多了一些噪点,也不影响阅读(利用OpenCV的中值滤波、高斯滤波或双边滤波对图片除噪点处理后效果反而变差)。用pdf编辑工具(如pdf24)将pdf文件逐页输出为图片保存到某个文件夹中,下面的程序可以将图片进行自适应对比度增强,然后转换成二值图片保存到另一文件夹中:

import cv2
import os
from PIL import Image
import numpy as npdef get_variance_mean(src_img, win_size):if src_img is None or win_size is None:print("函数参数错误。")return -1if win_size % 2 == 0:print("win_size参数应传入奇数。")return -1copyBorder_map = cv2.copyMakeBorder(src_img, win_size // 2, win_size // 2, win_size // 2, win_size // 2, cv2.BORDER_REPLICATE)shape = np.shape(src_img)local_mean = np.zeros_like(src_img)local_std = np.zeros_like(src_img)for i in range(shape[0]):for j in range(shape[1]):temp = copyBorder_map[i:i + win_size, j:j + win_size]# 计算均值和标准差mean_val, std_dev_val = cv2.meanStdDev(temp)# 提取均值和标准差的标量值local_mean[i, j] = mean_val[0, 0]local_std[i, j] = std_dev_val[0, 0]return local_mean, local_std# 自适应对比度增强
def adapt_contrast_enhancement(src_img, win_size, max_cg, rgb=True):if src_img is None or win_size is None or max_cg is None:print("函数参数错误。")return -1# 转换通道YUV_img = cv2.cvtColor(src_img, cv2.COLOR_BGR2YUV)  Y_Channel = YUV_img[:, :, 0]shape = np.shape(Y_Channel)meansGlobal = cv2.mean(Y_Channel)[0]localMean_map, localStd_map = get_variance_mean(Y_Channel, win_size)for i in range(shape[0]):for j in range(shape[1]):is_zero = localStd_map[i, j] == 0# 当分母localStd_map[i, j]为0时加上极小值1e-8,防止除以0错误cg = 0.2 * meansGlobal / (localStd_map[i, j]+(1e-8)*is_zero)if cg > max_cg:cg = max_cgelif cg < 1:cg = 1temp = Y_Channel[i, j].astype(float)temp = max(0, min(localMean_map[i, j] + cg * (temp - localMean_map[i, j]), 255))Y_Channel[i, j] = tempYUV_img[:, :, 0] = Y_Channelif rgb:return cv2.cvtColor(YUV_img, cv2.COLOR_YUV2RGB)else:return cv2.cvtColor(YUV_img, cv2.COLOR_YUV2BGR)if __name__ == '__main__':folder_path = "H:\\Download\\huanmie"output_path = "H:\\Download\\huanmie\\output"# 遍历文件夹中的所有文件for filename in os.listdir(folder_path):if filename.endswith(('.jpg', '.jpeg', '.png')):img = cv2.imread(os.path.join(folder_path, filename))# 自适应对比度增强dest_img = adapt_contrast_enhancement(img, 5, 10, False)# 转换为灰度图片gray_image = cv2.cvtColor(dest_img, cv2.COLOR_BGR2GRAY)# 设定阈值,可根据实际情况调整threshold_value = 188max_value = 255# 图片二值化_, binary_image = cv2.threshold(gray_image, threshold_value, max_value, cv2.THRESH_BINARY)# 将色彩模式转换成pillow的Image的色彩模式# binary_image_rgb = cv2.cvtColor(binary_image,cv2.COLOR_GRAY2RGB)# 构造pillow的Imageimage = Image.fromarray(binary_image)# 转换成灰度图片image = image.convert('L')pixels = image.load()# 获取图片的宽度和高度width, height = image.size# 消除ksize个像素大小的噪点及灰色噪点ksize = 1for y in range(ksize,height-ksize):for x in range(ksize,width -ksize):if ((pixels[x-ksize,y] == 255 and pixels[x+ksize,y] == 255 andpixels[x,y-ksize] == 255 and pixels[x,y+ksize] == 255) or pixels[x, y] > 100):pixels[x, y] = 255# 保存转换后的图片image.save(os.path.join(output_path, filename))print(f"已完成 {filename}的 转换。")# 设置响铃频率(赫兹)和持续时间(毫秒),转换完成后响铃提示frequency = 1000duration = 500os.system(f'Beep {frequency} {duration}')print('Done!')

代码中自适应对比度调整算法详情请参阅CSDN博主不用先生的文章:【图像处理】彩色图像自适应对比度增强(OpenCV实现)-CSDN博客,本文仅对该文的算法代码做了少量修改,消除了numpy版本升级后关于多维数组直接转换为标量引发的操作弃用警告以及计算cg时的除0错误警告。


http://www.mrgr.cn/news/77794.html

相关文章:

  • 【Oracle实战】文章导读
  • uniapp接入BMapGL百度地图
  • Vue前端开发-访问子组件对象中数据
  • MySQL中索引全详解
  • [Python3] Sanic 框架构建高并发的 Web 服务
  • java基础概念37:正则表达式2-爬虫
  • [模版总结] - 树的基本算法4 -最近公共祖先 LCA
  • 【大数据学习 | Spark-Core】yarn-client与yarn-cluster的区别
  • 浦语提示词工程实践(LangGPT版,服务器上部署internlm2-chat-1_8b,踩坑很多才完成的详细教程,)
  • 复习!!!
  • Spring |(二)IoC相关内容 | bean
  • Long noncoding RNAs and humandisease
  • 微服务即时通讯系统的实现(服务端)----(1)
  • 计算机视觉 1-8章 (硕士)
  • 动态内存管理
  • leetcode:112. 路径总和
  • AI+若依框架项目
  • el-tree 使用笔记
  • 【强化学习+组合优化】SAC + PointerNetwork 解决TSP问题
  • 常用数据结构详解
  • 【操作系统笔记】习题
  • 密码学11
  • 推荐一个基于协程的C++(lua)游戏服务器
  • Kubernetes的pod控制器
  • 大语言模型---什么是注意力机制?LlaMA 中注意力机制的数学定义
  • 002 MATLAB语言基础