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

news2024/11/26 21:36:29

使用墨水屏读书现在似乎越来越流行,这确实有一定的好处,例如基本不发热,电池续航时间超长,基本不能游戏所以有利于沉浸式阅读,还有不知道是不是真的有用的所谓防蓝光伤害。但是,如果阅读的书籍是扫描图片组成的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.0

file = 'test.pdf'
pdf_pages = fitz.open(file)
img_list = []
# 二值化阈值,可根据实际情况调整
threshold = 200
try:
    for page in pdf_pages:
        # 获取页面的图片数据,类型为pymupdf.Pixmap
        pixmap = page.get_pixmap()
        # 解码为 np.uint8类型的numpy.ndarray
        image_array = np.frombuffer(pixmap.samples, dtype=np.uint8).reshape(
            pixmap.height, pixmap.width, pixmap.n)
        # 转换为PIL.Image.Image,通过三行代码将pymupdf.Pixmap转换成了PIL.Image.Image
        image = 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] = 0
                else:
                    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 np

def get_variance_mean(src_img, win_size):
    if src_img is None or win_size is None:
        print("函数参数错误。")
        return -1

    if win_size % 2 == 0:
        print("win_size参数应传入奇数。")
        return -1

    copyBorder_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_cg
            elif cg < 1:
                cg = 1

            temp = 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] = temp

    YUV_img[:, :, 0] = Y_Channel

    if 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 = 188
            max_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的Image
            image = Image.fromarray(binary_image)
            # 转换成灰度图片
            image = image.convert('L')
            pixels = image.load()
            # 获取图片的宽度和高度
            width, height = image.size
            # 消除ksize个像素大小的噪点及灰色噪点
            ksize = 1
            for 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 and
                        pixels[x,y-ksize] == 255 and pixels[x,y+ksize] == 255) 
                      or pixels[x, y] > 100):
                        pixels[x, y] = 255
            # 中值滤波消除噪点,容易导致文字笔画残缺
            # image = cv2.medianBlur(np.array(image),3)
            # image = Image.fromarray(image)
            # 保存转换后的图片
            image.save(os.path.join(output_path, filename))
            print(f"已完成 {filename}的 转换。")

    # 设置响铃频率(赫兹)和持续时间(毫秒),转换完成后响铃提示
    frequency = 1000
    duration = 500
    os.system(f'Beep {frequency} {duration}')
    print('Done!')

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2248056.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端---HTML(一)

HTML_网络的三大基石和html普通文本标签 1.我们要访问网络&#xff0c;需不需要知道&#xff0c;网络上的东西在哪&#xff1f; 为什么我们写&#xff0c;www.baidu.com就能找到百度了呢&#xff1f; 我一拼ping www.baidu.com 就拼到了ip地址&#xff1a; [119.75.218.70]…

电影风格城市夜景旅拍Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色教程 电影风格城市夜景旅拍通过 Lightroom 调色&#xff0c;将城市夜晚的景色打造出如同电影画面般的质感和氛围。以独特的色彩和光影处理&#xff0c;展现出城市夜景的魅力与神秘。 预设信息 调色风格&#xff1a;电影风格预设适合类型&#xff1a;人像&#xff0c;街拍…

【多模态】Flamingo模型技术学习

Flamingo模型技术学习 前言Flamingo——支持上下文学习的多模态模型模型架构模型架构——Resampler模型架构——插入到LLM的cross-attention层 代码查看——masked cross-attentionnote 前言 最近多模态模型特别火&#xff0c;从头开始学习&#xff01;在前面写的几篇里面学习了…

C 语言学习-06【指针】

1、目标单元与简介存取 直接访问和间接访问 #include <stdio.h>int main(void) {int a 3, *p;p &a;printf("a %d, *p %d\n", a, *p);*p 10;printf("a %d, *p %d\n", a, *p);printf("Enter a: ");scanf("%d", &a)…

ctfshow单身杯2024wp

文章目录 ctfshow单身杯2024wp签到好玩的PHPezzz_sstiez_inject ctfshow单身杯2024wp 签到好玩的PHP 考点&#xff1a;序列化反序列化 <?phperror_reporting(0);highlight_file(__FILE__);class ctfshow {private $d ;private $s ;private $b ;private $ctf ;public …

超高流量多级缓存架构设计!

文章内容已经收录在《面试进阶之路》&#xff0c;从原理出发&#xff0c;直击面试难点&#xff0c;实现更高维度的降维打击&#xff01; 文章目录 电商-多级缓存架构设计多级缓存架构介绍多级缓存请求流程负载均衡算法的选择轮询负载均衡一致性哈希负载均衡算法选择 应用层 Ngi…

【Mybatis】@Param注解 resultMap手动映射

文章目录 一、映射文件参数二、查询映射2-1 一对一2-2 一对多2-3 总结 一、映射文件参数 Param 注解官方文档解释 1、单个参数&#xff08;对象&#xff09;不使用注解 public int save(User user);<!-- 添加用户 --> <insert id"save" parameterType&quo…

Unreal从入门到精通之如何绘制用于VR的3DUI交互的手柄射线

文章目录 前言实现方式MenuLaser实现步骤1.Laser和Cursor2.移植函数3.启动逻辑4.检测射线和UI的碰撞5.激活手柄射线6.更新手柄射线位置7.隐藏手柄射线8.添加手柄的Trigger监听完整节点如下:效果图前言 之前我写过一篇文章《Unreal5从入门到精通之如何在VR中使用3DUI》,其中讲…

风尚云网前端学习:一个简易前端新手友好的HTML5页面布局与样式设计

风尚云网前端学习&#xff1a;一个简易前端新手友好的HTML5页面布局与样式设计 简介 在前端开发的世界里&#xff0c;HTML5和CSS3是构建现代网页的基石。本文将通过一个简单的HTML5页面模板&#xff0c;展示如何使用HTML5的结构化元素和CSS3的样式特性&#xff0c;来创建一个…

STM32WB55RG开发(5)----监测STM32WB连接状态

STM32WB55RG开发----5.生成 BLE 程序连接手机APP 概述硬件准备视频教学样品申请源码下载参考程序选择芯片型号配置时钟源配置时钟树RTC时钟配置RF wakeup时钟配置查看开启STM32_WPAN条件配置HSEM配置IPCC配置RTC启动RF开启蓝牙LED配置设置工程信息工程文件设置参考文档SVCCTL_A…

RK356x-10:串口(uart)配置与调试

本文记录RK3566/RK3568通用异步串口(uart)的配置与调试过程 1. 配置uart节点 1.1 在rk3568.dtsi中已经定义了uart0,uart1...&#xff0c;根据电路图&#xff0c;确定需要使用的是哪个串口&#xff0c;比如我使用的是uart0&#xff0c;在另外一个与我们自己主板相关的dtsi文件&…

【C++】从C到C++

C和C一些语法区别 1.三目运算符&#xff1a;在C语言中返回的是一个常量&#xff0c;是不能被赋值的&#xff1b;而C中返回的是变量&#xff0c;可以被赋值 2.C中的函数必须要写返回值类型 3.在全局下&#xff0c;C不允许int a;和int a10;等这种重定义二义性操作 4.在C中不要…

WSL安装不同版本ubuntu(已有ubuntu20.04,再装ubuntu18.04)

参考&#xff1a; 如何在 WSL 中删除指定版本的 Ubuntu&#xff08;以删除 Ubuntu 22.04 为例&#xff09;_wsl卸载某个-CSDN博客 已有ubuntu20.04&#xff0c;现在再安装一个ubuntu18.04 直接参考下面我写的链接的第四步&#xff0c;前面的步骤都不需要再做了 Win11安装WSL…

《硬件架构的艺术》笔记(七):处理字节顺序

介绍 本章主要介绍字节顺序的的基本规则。&#xff08;感觉偏软件了&#xff0c;不知道为啥那么会放进《硬件架构的艺术》这本书&#xff09;。 定义 字节顺序定义数据在计算机系统中的存储格式&#xff0c;描述存储器中的MSB和LSB的位置。对于数据始终以32位形式保存在存储器…

wkhtmltopdf的安装与使用

本文来记录下wkhtmltopdf的安装与使用 文章目录 概述下载路径安装配置wkhtmltopdf 参数详解代码实现本文小结 概述 将html转为pdf的组件有很多&#xff0c;但是还没有哪一款能达到这个效果&#xff0c;其只要原因是wkhtmltopdf使用webkit网页渲染引擎开发的用来将 html转成 pdf…

241125学习日志——[CSDIY] [InternStudio] 大模型训练营 [17]

CSDIY&#xff1a;这是一个非科班学生的努力之路&#xff0c;从今天开始这个系列会长期更新&#xff0c;&#xff08;最好做到日更&#xff09;&#xff0c;我会慢慢把自己目前对CS的努力逐一上传&#xff0c;帮助那些和我一样有着梦想的玩家取得胜利&#xff01;&#xff01;&…

C++ High Performance(壹)

目录 前言 C概述 1.零开销原则 2.值语义 3.C函数中参数的含义 C必备技能 1.在函数返回值中使用auto 2.使用decltype(auto)转返回类型 3.对变量使用auto 4.常量引用 5.指针的常量传播 6.移动语义 7.资源获取与五法则 8.默认移动语义和零法则 9.将&&…

数据库的联合查询

数据库的联合查询 简介为什么要使⽤联合查询多表联合查询时MYSQL内部是如何进⾏计算的构造练习案例数据案例&#xff1a;⼀个完整的联合查询的过程 内连接语法⽰例 外连接语法 ⽰例⾃连接应⽤场景示例表连接练习 ⼦查询语法单⾏⼦查询多⾏⼦查询多列⼦查询在from⼦句中使⽤⼦查…

vue 预览pdf 【@sunsetglow/vue-pdf-viewer】开箱即用,无需开发

sunsetglow/vue-pdf-viewer 开箱即用的pdf插件sunsetglow/vue-pdf-viewer, vue3 版本 无需多余开发&#xff0c;操作简单&#xff0c;支持大文件 pdf 滚动加载&#xff0c;缩放&#xff0c;左侧导航&#xff0c;下载&#xff0c;页码&#xff0c;打印&#xff0c;文本复制&…

【zookeeper03】消息队列与微服务之zookeeper集群部署

ZooKeeper 集群部署 1.ZooKeeper 集群介绍 ZooKeeper集群用于解决单点和单机性能及数据高可用等问题。 集群结构 Zookeeper集群基于Master/Slave的模型 处于主要地位负责处理写操作)的主机称为Leader节点&#xff0c;处于次要地位主要负责处理读操作的主机称为 follower 节点…