基于pyqt5+opencv实现16位tif影像转jpg

news2024/11/15 1:26:40

现在大部分图像软件都支持tiff影像的浏览,但都是仅限于8位的影像,对应CV16U类型的tiff影像并不支持(这需要专业的gis软件才可进行操作)。为了便捷操作,故此基于pyqt5+opencv实现16位tif影像转jpg的软件。
本博文涉及基于ui文件直接构建界面、实现文件拖拽打开、按钮组状态切换、点击图像弹出文件对话框功能。对于16位tif影像转jpg,实现了3种转换函数,分别为normalization_img、min_max_normalization_img、cut_normalization_img。其中cut_normalization_img函数可以滤除掉tiff影像中的异常值,可以在格式转换时增强图像效果的稳定性。

关于pyqt5的使用可以参考 https://hpg123.blog.csdn.net/article/details/131564563?spm=1001.2014.3001.5502

相关依赖库安装代码

pip install  python-opencv
pip install pyqt5
pip install pyqt5-tools

1、核心代码

对于16U的tiff影像可以使用opencv读取 代码示例:cv2.imread(path,-1),然后利用代码,修改图像的数据类型,并对图像的值域进行调整,即可实现将16U的tiff影像转为8U的影像。

1.1 基本转换

将16U的数据转换为8U有两种最简单的方式,即为归一化,具体如normalization_img函数代码所示,先将数据的值域压缩到0 ~ 1,然后再调整到 0 ~ 255。这种转换可能会存在某种问题,例如数据的整体值偏高,这样转化后就看不到差异了,故此又设计了min_max_normalization_img函数。先将数据的最小值调整为0,然后再进行归一化。

import cv2
import numpy as np

def normalization_img(img):
    img=img/img.max()
    img=img*255
    img=img.astype(np.uint8)
    return img

def min_max_normalization_img(img):
    img=img-img.min()
    return normalization_img(img)

1.2 截断转换

对于某些特殊的行业数据,可能存在较多的噪声,其最大值和最小值并不能真实反映数据的情况(可能为传感器故障),从而需要对数据值域的频率进行统计,找到噪声的阈值(最大值阈值、最小值阈值),将大于最大值阈值的数值修改为最大值阈值,将小于最小值阈值的数值修改为最小值阈值。然后再将值域压缩到0~255。

def cut_normalization_img(img):
	#统计每个值出现的频率
    img=img.astype(np.int32)
    frequence=np.bincount(img.reshape(-1)) 
    #设置用于计算最大值阈值,最小值阈值的参数
    all_pixs=frequence.sum()
    min_rate=0.005 #从左往右累加统计各个值域的频率,频率值大于min_rate时,则为最小值阈值
    max_rate=0.005#从右往左累加统计各个值域的频率,频率值大于min_rate时,则为最大值阈值
    now_min_rate=0
    now_max_rate=0

    min_index=0 #最小值阈值
    while now_min_rate>=min_rate:
        now_pixs=frequence[:min_index].sum()
        now_min_rate=now_pixs/all_pixs
        min_index+=1

    max_index=-1 #最小值阈值
    while now_max_rate>=max_rate:
        now_pixs=frequence[max_index:].sum()
        now_max_rate=now_pixs/all_pixs
        max_index-=1
    max_index=max_index+frequence.shape[0]#修正最小值阈值的表达方式
    
    #将数据的值域调整为0~(max_index-min_index)
    img[img>max_index]=max_index
    img[img<min_index]=min_index
    img=img-min_index

    #将数据的值域调整为0~255
    img=img.astype(np.float32)
    img=img/(max_index-min_index)
    img=img*255
    img=img.astype(np.uint8)
    return img

以上两端代码合并再一起保存为tif2jpg.py

2、软件实现

2.1 界面设计

页面布局中的关系如下,这里需要注意的是:其中label控件和pushButton控件是叠加在一起的,只不过pushButton的不透明度被设置0(通过在控件上单击右键=》修改样式表=》设置其样式为background-color: rgba(255, 255, 255, 0);)。
在本软件中博主自行将3个button放置到一个父容器内,意图实现类似单选按钮的功能。同时,关于图像的显示和文本的显示都使用label控件实现。然后,为了能让用户点选图片,故再label控件上面叠加了一个透明的pushButton控件。
在这里插入图片描述

2.2 事件设置

按钮组切换实现 在pyqt中并没有按钮组控件,为此只能将多个button强制进行组合。博主所实现的按钮组还包含一个名为groupBtnLabel的QLabel控件,在点击按钮后groupBtnLabel控件所显示的文本也会相应切换。

在ui初始化时按钮组的初始化代码如下所示,通过btn_group对象设置按钮上的文本和击中按钮后要显示的文本。

btn_group={
           "ZeroMax归一化":"将0到maxValue之间的值缩放到0~255",
           "MinMax归一化":"将minValue到maxValue之间的值缩放到0~255",
           "截断归一化":"统计数值出现的频率,将最大值和最小值频率低于0.5%的数据进行滤除",
           }
		#设置窗口标题
        self.ui.setWindowTitle('16位tif转8位软件')
        # 绑定按钮组事件
        bgkl=list(btn_group.keys())
        self.ui.pushButton_1.setText(bgkl[0])#重新为3个按钮设置文本 
        self.ui.pushButton_2.setText(bgkl[1])
        self.ui.pushButton_3.setText(bgkl[2])
        self.ui.pushButton_1.clicked.connect(self.groupBtnClick)#为3个按钮绑定相同的槽函数
        self.ui.pushButton_2.clicked.connect(self.groupBtnClick)
        self.ui.pushButton_3.clicked.connect(self.groupBtnClick)
        #设置按钮组的默认设置
        self.btn_type=self.ui.pushButton_1.text() #设置默认选择pushButton_1
        self.ui.pushButton_1.setStyleSheet("background-color: rgb(0, 255, 127);")#设置默认选择pushButton_1的样式
        self.ui.groupBtnLabel.setText(btn_group[self.btn_type])#设置显示对应的文本提示

groupBtnClick的实现如下,在按钮被点击后先将所有的按钮设置为背景色,然后再将击中的按钮设置为选中色,同时根据击中按钮的文本值设置groupBtnLabel的值。

    def groupBtnClick(self):
        self.ui.pushButton_1.setStyleSheet("background-color: rgb(230, 230, 230);")
        self.ui.pushButton_2.setStyleSheet("background-color: rgb(230, 230, 230);")
        self.ui.pushButton_3.setStyleSheet("background-color: rgb(230, 230, 230);")
        buttonButton = self.ui.sender() # 获得信号发射的控件
        buttonButton.setStyleSheet("background-color: rgb(0, 255, 127);")
        self.btn_type=buttonButton.text()
        self.ui.groupBtnLabel.setText(btn_group[self.btn_type])
        print(self.btn_type)

文件拖拽实现 博主这里是使用ui文件直接构建界面,故此在实现文件拖拽时略有复杂,需要手动对ui.dragEnterEvent和self.ui.dropEvent进行事件绑定。

        #设置支持图片拖拽
        self.ui.setAcceptDrops(True)
        self.ui.dragEnterEvent=self.dragEnterEvent
        self.ui.dropEvent=self.dropEvent

    def dropEvent(self, evn):
        # print(f'鼠标放开 {evn.posF()}')
        path = evn.mimeData().text()
        if path.lower().endswith((".jpg",".jpeg",".png",".bmp",".tif",".tiff")):
            path=path.replace('file:///','')
            self.ui.label_state.setText(path)
            self.ui.label_state.setStyleSheet("color: rgb(28, 81, 255);")#设置字体颜色
            self.ui.label.setText(path+"处理中。。。")
            print('文件路径:\n' + path)
            thread_new = ServerThread(self.ui,path,self.btn_type)
            thread_new.start()
            thread_new.message.connect(self.show_result_img)
        else:
            self.ui.label_state.setText(path+"  不支持的文件类型!\n请输入jpg、png、png、tif格式的图片")
            self.ui.label_state.setStyleSheet("color: rgb(0, 240, 10);")#设置字体颜色
    def dragEnterEvent(self, evn):
        #print('鼠标拖入窗口')
        evn.accept()

点击图片弹出文件对话框 由于博主使用QLabel控件展示图片,故此无法针对QLabel设置点击事件,从而在其上面叠加了一个透明的pushButton控件(二者叠加在一起,点击QLabel必然要触发pushButton的click事件)。

		#实现点击图片,弹出文件对话框
        self.ui.pushButton.clicked.connect(self.select_file)
    def select_file(self):
        filename = QFileDialog.getOpenFileNames(self.ui, '选择图像', os.getcwd(), "Tiff Files(*.tif;*.tiff);Image Files(*.jpg;*.jpeg;*.png);")
        # 输出文件,查看文件路径
        if len(filename)==0:
            return
        if len(filename[0])==0:
            return
        path = filename[0][0]
        self.ui.label_state.setText(path)
        self.ui.label.setText(path+"处理中。。。")
        print('文件路径:\n' + path)
        thread_new = ServerThread(self.ui,path,self.btn_type)
        thread_new.start()
        thread_new.message.connect(self.show_result_img)

2.3 线程阻塞更新UI

由于某些tif影像size过大,在ui线程进行耗时运算必然使软件卡死,故此有必要将任务移动到子线程进行计算,计算完后再由子线程通知UI线程更新界面。
所实现的子线程代码如下所示,其中的parent参数是必要的(让子线程依附在MainWindow中),path,deal_type是子程序处理任务时的参数,具体对应run函数中的代码。
其中需要注意的时message = pyqtSignal(str)创建了一个信号槽对象(由ui线程注入),用于通知ui线程更新界面 在这里时对应self.show_result_img函数

class ServerThread(QThread):
    #信号槽,用于与ui界面进行交互
    message = pyqtSignal(str)
 
    def __init__(self,parent, path,deal_type):
        super(ServerThread, self).__init__(parent)
        self.parent = parent
        self.working = True
        self.path=path
        self.deal_type=deal_type
 
    def __del__(self):
        self.working = False
        self.wait()
 
    def run(self):
        img=cv2.imread(self.path,-1)
        img=img.astype(np.float32)
        if self.deal_type=="截断归一化":
            img=cut_normalization_img(img)
        elif self.deal_type=="ZeroMax归一化":
            img=normalization_img(img)
        elif self.deal_type=="MinMax归一化":
            img=min_max_normalization_img(img)
        cv2.imwrite(self.path+".jpg",img)
        #通知UI界面进行更新
        self.message.emit(self.path+".jpg")

具体使用如下所示,在点击图片时触发select_file函数,从而创建ServerThread对象,然后启动子线程,并使用thread_new.message.connect(self.show_result_img)将ui更新函数注入到thread_new中。

    def select_file(self):
        filename = QFileDialog.getOpenFileNames(self.ui, '选择图像', os.getcwd(), "Tiff Files(*.tif;*.tiff);Image Files(*.jpg;*.jpeg;*.png);")
        # 输出文件,查看文件路径
        if len(filename)==0:
            return
        if len(filename[0])==0:
            return
        path = filename[0][0]
        self.ui.label_state.setText(path)
        self.ui.label.setText(path+"处理中。。。")
        print('文件路径:\n' + path)
        thread_new = ServerThread(self.ui,path,self.btn_type)
        thread_new.start()
        thread_new.message.connect(self.show_result_img)
    def show_result_img(self,path):
        self.ui.label_state.setText(path+"处理完成!!!!!!!!")
        self.ui.label_state.setStyleSheet("color: rgb(28, 81, 255);")#设置字体颜色
        pixmap = QPixmap(path)
        self.ui.label.setPixmap (pixmap)  # 在label上显示图片
        self.ui.label.setScaledContents (True)  # 让图片自适应label大小

3、代码及效果与软件

3.1 代码

所设计的代码都在 https://download.csdn.net/download/a486259/88014867 ,创作不易,希望各位支持一下。当然,核心代码在上述博客中已经完整描述了,对于pyqt5的使用有困难可以参考https://hpg123.blog.csdn.net/article/details/131564563 ,跟着做一遍 。

3.2 效果

在这里插入图片描述
在这里插入图片描述

3.3 软件

使用pyinstaller可以将py程序打包为exe程序,但是会连带很多其他不相干的库,为此需要在虚拟环境中进行打包,这里使用pipenv构建虚拟环境。

pip install pipenv
#pip install pyinstaller

激活虚拟环境

执行 pipenv shell 进入虚拟环境

(base) C:\Users\Administrator\Desktop\fsdownload>pipenv shell
Creating a virtualenv for this project...
Pipfile: C:\Users\Administrator\Desktop\fsdownload\Pipfile
Using default python from C:\anaconda\python.exe (3.9.12) to create virtualenv...
[ ===] Creating virtual environment...created virtual environment CPython3.9.12.final.0-64 in 629ms
  creator CPython3Windows(dest=C:\Users\Administrator\.virtualenvs\fsdownload-dZqvdOWt, clear=False, no_vcs_ignore=False, global=False)
  seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=C:\Users\Administrator\AppData\Local\pypa\virtualenv)
    added seed packages: pip==23.1.2, setuptools==67.8.0, wheel==0.40.0
  activators BashActivator,BatchActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator

Successfully created virtual environment!
Virtualenv location: C:\Users\Administrator\.virtualenvs\fsdownload-dZqvdOWt
Creating a Pipfile for this project...
Launching subshell in virtual environment...
Microsoft Windows [版本 10.0.19044.1320]
(c) Microsoft Corporation。保留所有权利。

(fsdownload-dZqvdOWt) (base) C:\Users\Administrator\Desktop\fsdownload>

安装依赖包

使用pipenv instal package_name 进行包安装

pipenv install  python-opencv
pipenv install pyqt5
pipenv install pyqt5-tools
pipenv install pyinstaller

导出为可执行程序

打包为文件夹形式(-D),不显示控制台(-w)

pyinstaller -D -w maintool.py

打包为单个文件形式(-F),不显示控制台(-w)

pyinstaller -F -w maintool.py

导出后的软件下载地址如下,为单个文件形式,约80m左右
https://download.csdn.net/download/a486259/88015083

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

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

相关文章

OpenCV4通道的分离split(),通道的合并merge(),通道的混合mixChannels()

文章目录 1、通道的分离函数 split()函数原型&#xff1a;&#xff08;1&#xff09;函数原型一&#xff1a;用 Mat型数组 Mat mvbegin[3]存储分离后的图像&#xff1b;输入参数&#xff1a; &#xff08;2&#xff09;函数原型二&#xff1a;用 vector容器 vector <Mat>…

科技中心PMO的建设与实践︱德邦证券PMO专家张鉴庭

德邦证券科技中心PMO专家张鉴庭先生受邀为由PMO评论主办的2023第十二届中国PMO大会演讲嘉宾&#xff0c;演讲议题&#xff1a;科技中心PMO的建设与实践。大会将于8月12-13日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&#xff1a; 在数字化转型的背景下&#xff0c…

jvm对象创建和内存分配优化

一、创建对象过程 1、类加载检测 虚拟机遇到一条new指令时&#xff0c;首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用&#xff0c;并且检查这个符号引用代表的类是否是否已被加载、解析和初始化过。如果没有&#xff0c;那必须先执行相应的类加载过程。 …

ModaHub魔搭社区:向量数据库Milvus Lite的优势和适配场景

目录 Milvus Lite 的优势 Milvus Lite 的适配场景 如何安装、部署和使用 Milvus Lite? 总结 想要体验世界上最快的向量数据库&#xff1f;缺少专业的工程师团队作为支撑&#xff1f;Milvus 安装环境受限&#xff1f; 别担心&#xff0c;轻量版 Milvus 来啦&#xff01; …

火爆全网,python自动化测试 parametrize参数化+allure测试报告(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 pytest的参数化&a…

1763_gcc编译c语言makefile自动生成工具的Perl实现_Linux

全部学习汇总&#xff1a; GreyZhang/g_makefile: Learn makefile from all kinds of tutorials on the web. Happy hacking and lets find an common way so we may dont need to touch makefile code any more! (github.com) 其实&#xff0c;调试完这个之后觉得之前Windows上…

开源:老朋友,新棋局

在软件开发领域&#xff0c;开源已经成为一股强大的力量&#xff0c;为企业带来了巨大的好处。我深知开源的价值和影响力。其中之一就是降低开发成本。传统的软件开发往往需要庞大的开发团队和昂贵的授权费用&#xff0c;但开源软件将这一切变得通俗易懂。 避免了重复造轮子&a…

ROS-Moveit!配置

文章目录 1. SW2URDF2.Moveit下载及初始化3.自碰撞矩阵 Self-Collisions4.虚拟关节 Virtual Joints&#xff08;不配置&#xff09;5.规划组 Planning Groups添加机械臂规划组添加夹爪规划组 6.机器人姿态 Robot Pose7.末端执行器 End Effectors8.作者信息 Author Information9…

(秋招)面激光slam必备知识--scan context

scan context是一个描述场景的描述符&#xff0c;它之前不是用在slam上面的&#xff0c;但是有人将它用到激光slam上面&#xff0c;发现还可以&#xff0c;于是这个scan context就用来进行激光slam的位置识别(做闭环用的)。 ​ 编辑切换为居中 添加图片注释&#xff0c;不超过…

Python基础综合案例-数据可视化(地图)

今天给大家带来的是Python综合实战开发的数据可视化操作 通过python实现对数据的分析、可视化 数据来源:线上公布数据&#xff0c;需要可私信 前期准备工作&#xff1a;Python可视化准备工作 前期模块安装等前期基础的准备工作大家可以看我之前的文章讲解&#xff0c;有问题可…

< 每日算法 - JavaScript解析:一文解决 “ 买卖股票 ” 系列算法题 >

每日算法 - JavaScript解析&#xff1a;一文解决 “ 买卖股票 ” 系列算法题 一、基础题目> 题目> 解题思路定义操作定义状态动态规划值所需变量完整代码 二、添加条件&#xff1a;当交易次数为 ∞ 时> 题目> 解决思路 三、添加条件&#xff1a;当交易次数为 K nu…

小机器人在现实世界中学会快速驾驶

小机器人在现实世界中学会快速驾驶 —强化学习加上预训练让机器人赛车手加速前进— Without a lifetime of experience to build on like humans have (and totally take for granted), robots that want to learn a new skill often have to start from scratch. Reinforceme…

【案例教程】GIS在地质灾害危险性评估与灾后重建中的实践技术应用及python机器学习灾害易发性评价模型建立与优化

地质灾害是指全球地壳自然地质演化过程中&#xff0c;由于地球内动力、外动力或者人为地质动力作用下导致的自然地质和人类的自然灾害突发事件。由于降水、地震等自然作用下&#xff0c;地质灾害在世界范围内频繁发生。我国除滑坡灾害外&#xff0c;还包括崩塌、泥石流、地面沉…

AI对话宝-智能AI在线问答写作

AI对话宝的工作原理是基于自然语言处理技术。当用户与其进行交互时&#xff0c;AI对话宝会根据用户的输入&#xff0c;通过算法和模型来理解用户的意图&#xff0c;并给出相应的回答&#xff0c;并且系统还可以不断学习并优化其回答&#xff0c;从而提高其交互的效率和准确性。…

批量提取目录下的所有文件名称放至Excel表格中,方便快捷,快来试试吧

批量提取目录下的所有文件名称放至Excel表格中&#xff0c;方便快捷&#xff0c;快来试试吧 Sina Visitor Systemhttps://weibo.com/tv/show/1034:4920955869265935?fromold_pc_videoshow

binwalk-解包工具

一、binwalk介绍 二、安装binwalk # 1. 安装依赖和binwalk ​git clone https://github.com/ReFirmLabs/binwalk.git cd binwalk sudo python ./setup.py uninstall # 如果您有以前安装的 Binwalk 版本&#xff0c;建议您在升级之前将其卸载 sudo ./deps.sh # 安装依赖项 sud…

C语言学习(三十五)---动态内存练习题与柔性数组

经过前面的内容&#xff0c;我们已经对动态内存的知识已经有了相当多了了解&#xff0c;今天我们再做几道有关动态内存的练习题&#xff0c;然后再介绍一下柔性数组&#xff0c;好了&#xff0c;话不多说&#xff0c;开整&#xff01;&#xff01;&#xff01; 动态内存练习题…

上海亚商投顾:沪指失守3200点 两市成交不足8000亿

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 沪指今日延续调整&#xff0c;失守3200点关口&#xff0c;深成指、创业板指盘中均跌超1%。AI概念股集体下挫&#…

你会做接口测试吗?接口测试面试题盲扫(附答案)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 为什么要做接口测…

【跑实验06】如何数据集中加入图像尺寸?如何把tuple格式的坐标按顺序写成四列数据?如何把某一列放到最后?

文章目录 一、如何数据集中加入图像尺寸&#xff1f;二、如何把tuple格式的坐标按顺序写成四列数据&#xff1f;三、如何把某一列放到最后&#xff1f; 一、如何数据集中加入图像尺寸&#xff1f; 部分核心代码如下&#xff1a; image_files [filename for filename in os.l…