双线性插值算法缩放图片,部分图片出现黑边的解决办法

            因工作需要使用软件方法缩放PNG图片,询问chatgpt拿到了c++双线性插值算法,开始很顺利,整理一下代码,封装一下接口,就可以使用了,效果还不错,马上编译发给测试组测试,测试发现有一些图片缩放后出现黑边,还有一些图片缩放时程序崩溃了。有问题的代码如下。

BOOL hbmp_stretch_Bilinear(HBITMAP hbmp_dst, HBITMAP hbmp_src)
{BITMAP info_src, info_dst;GetObject(hbmp_src, sizeof(BITMAP), &info_src);GetObject(hbmp_dst, sizeof(BITMAP), &info_dst);int srcWidth = info_src.bmWidth;int srcHeight = info_src.bmHeight;int dstWidth = info_dst.bmWidth; // 新的图像宽度int dstHeight = info_dst.bmHeight; // 新的图像高度float rateX = (float)(srcWidth - 1) / (float)(dstWidth - 1);float rateY = (float)(srcHeight - 1) / (float)(dstHeight - 1);BYTE* bits = (BYTE*)info_src.bmBits;float srcX, srcY;int x0, x1, y0, y1;int x, y;float fL, fR, fT, fB, fTL, fTR, fBL, fBR;for (y = 0; y < dstHeight; y++) {for (x = 0; x < dstWidth; x++) {srcX = (float)x * rateX;srcY = (float)y * rateY;x0 = (int)srcX;y0 = (int)srcY;x1 = x0 + 1;y1 = y0 + 1;BYTE* pTL = &bits[info_src.bmWidthBytes * y0 + (x0 << 2)];BYTE* pTR = &bits[info_src.bmWidthBytes * y0 + (x1 << 2)];BYTE* pBL = &bits[info_src.bmWidthBytes * y1 + (x0 << 2)];BYTE* pBR = &bits[info_src.bmWidthBytes * y1 + (x1 << 2)];BYTE* pTarget = &((BYTE*)info_dst.bmBits)[info_dst.bmWidthBytes * y + (x << 2)];fR = srcX - x0;fB = srcY - y0;fL = (1.0f - fR);fT = (1.0f - fB);fTL = fL * fT;fTR = fR * fT;fBL = fL * fB;fBR = fR * fB;pTarget[0] = (BYTE)(fTL * (float)pTL[0] + fTR * (float)pTR[0] + fBL * (float)pBL[0] + fBR * (float)pBR[0]);pTarget[1] = (BYTE)(fTL * (float)pTL[1] + fTR * (float)pTR[1] + fBL * (float)pBL[1] + fBR * (float)pBR[1]);pTarget[2] = (BYTE)(fTL * (float)pTL[2] + fTR * (float)pTR[2] + fBL * (float)pBL[2] + fBR * (float)pBR[2]);pTarget[3] = (BYTE)(fTL * (float)pTL[3] + fTR * (float)pTR[3] + fBL * (float)pBL[3] + fBR * (float)pBR[3]);}}return true;
}

         用有问题的图片单步跟踪执行,很快锁定了有问题的代码行:

    float rateX = (float)(srcWidth - 1) / (float)(dstWidth - 1);float rateY = (float)(srcHeight - 1) / (float)(dstHeight - 1);。。。for (y = 0; y < dstHeight; y++) {for (x = 0; x < dstWidth; x++) {srcX = (float)x * rateX;srcY = (float)y * rateY;x0 = (int)srcX;y0 = (int)srcY;

           x0是源图像素点列坐标,代入转换:

x0=(int)srcX=x*rateX=x*(srcWidth - 1)/(dstWidth - 1)

srcWidth:源图片的宽度         dstWidth:目标图片的宽度

        当到达像素行尾时,x=dstWidth-1,根据上面的公式,x0的理论值是srcWidth - 1,正好覆盖源图片的一行。但是rateX转换浮点数时有可能会发生截断误差,2个整数相除,其结果有可能是有限不循环小数,也有可能是无限循环小数,第一种结果小数位数超过7位与第二种结果都会产生截断,当发生向下截断时,x=dstWidth-1,x0的值是srcWidth - 2,x1=x0+1的值不会越界,其它情况x0的值是srcWidth - 1,x1=x0+1的值越界,所以有些图片正常,有些图片应用程序因为内存越界而崩溃。同理,计算y0也存在同样的问题。找到了问题原因,可以在计算rateX、rateY时额外加上或者减去一个微小值,让rateX、rateY的截断发生在同一方向(向上截断或者向下截断),相应的得到两种解决方案。

解决方案一

        在计算rateX、rateY时额外加上0.0001,让rateX、rateY的截断全部是向上截断,没有向下截断。

        float rateX = (float)(srcWidth -1 + 0.0001) / (float)(dstWidth - 1);
        float rateY = (float)(srcHeight -1 + 0.0001) / (float)(dstHeight - 1);

此时遍历到行尾时x1会产生越界,遍历到最后一行时y1会产生越界,因此行尾与最后一行移出循环体,做特别的处理。代码如下:

BOOL hbmp_stretch_Bilinear_border(HBITMAP hbmp_dst, HBITMAP hbmp_src)
{BITMAP info_src, info_dst;GetObject(hbmp_src, sizeof(BITMAP), &info_src);GetObject(hbmp_dst, sizeof(BITMAP), &info_dst);int srcWidth = info_src.bmWidth;int srcHeight = info_src.bmHeight;int dstWidth = info_dst.bmWidth; // 新的图像宽度int dstHeight = info_dst.bmHeight; // 新的图像高度float rateX = (float)(srcWidth -1 + 0.0001) / (float)(dstWidth - 1);float rateY = (float)(srcHeight -1 + 0.0001) / (float)(dstHeight - 1);BYTE* bits = (BYTE*)info_src.bmBits;float srcX, srcY;int x0, x1, y0, y1;int x, y;float fL, fR, fT, fB, fTL, fTR, fBL, fBR;for (y = 0; y < dstHeight-1; y++) {for (x = 0; x < dstWidth-1; x++) {srcX = (float)x * rateX;srcY = (float)y * rateY;x0 = (int)srcX;y0 = (int)srcY;x1 = x0 + 1;y1 = y0 + 1;BYTE* pTL = &bits[info_src.bmWidthBytes * y0 + (x0 << 2)];BYTE* pTR = &bits[info_src.bmWidthBytes * y0 + (x1 << 2)];BYTE* pBL = &bits[info_src.bmWidthBytes * y1 + (x0 << 2)];BYTE* pBR = &bits[info_src.bmWidthBytes * y1 + (x1 << 2)];BYTE* pTarget = &((BYTE*)info_dst.bmBits)[info_dst.bmWidthBytes * y + (x << 2)];fR = srcX - x0;fB = srcY - y0;fL = (1.0f - fR);fT = (1.0f - fB);fTL = fL * fT;fTR = fR * fT;fBL = fL * fB;fBR = fR * fB;pTarget[0] = (BYTE)(fTL * (float)pTL[0] + fTR * (float)pTR[0] + fBL * (float)pBL[0] + fBR * (float)pBR[0]);pTarget[1] = (BYTE)(fTL * (float)pTL[1] + fTR * (float)pTR[1] + fBL * (float)pBL[1] + fBR * (float)pBR[1]);pTarget[2] = (BYTE)(fTL * (float)pTL[2] + fTR * (float)pTR[2] + fBL * (float)pBL[2] + fBR * (float)pBR[2]);pTarget[3] = (BYTE)(fTL * (float)pTL[3] + fTR * (float)pTR[3] + fBL * (float)pBL[3] + fBR * (float)pBR[3]);}int x_src = srcWidth - 1;int y_src = (int)y * rateY;BYTE* src = &bits[info_src.bmWidthBytes * y_src + (x_src << 2)];BYTE* target = &((BYTE*)info_dst.bmBits)[info_dst.bmWidthBytes * y + ((dstWidth - 1) << 2)];target[0] = src[0];target[1] = src[1];target[2] = src[2];target[3] = src[3];}for (int x_dst = 0; x_dst < dstWidth; x_dst++) {int x_src = (int)x_dst * rateX;BYTE* src = &bits[info_src.bmWidthBytes * (srcHeight-1) + (x_src << 2)];BYTE* target = &((BYTE*)info_dst.bmBits)[info_dst.bmWidthBytes * (dstHeight-1)+(x_dst << 2)];target[0] = src[0];target[1] = src[1];target[2] = src[2];target[3] = src[3];}return true;
}

解决方案二

         在计算rateX、rateY时额外减去0.0001,让rateX、rateY的截断全部是向下截断,没有向上截断。

        float rateX = (float)(srcWidth -1 - 0.0001) / (float)(dstWidth - 1);
        float rateY = (float)(srcHeight -1 - 0.0001) / (float)(dstHeight - 1);

       这种方法巧妙利用在行尾与最后一行权重fBL、fBR约等于1.0,从而避免特别处理行尾与最后一行。

BOOL hbmp_stretch_Bilinear(HBITMAP hbmp_dst, HBITMAP hbmp_src)
{BITMAP info_src, info_dst;GetObject(hbmp_src, sizeof(BITMAP), &info_src);GetObject(hbmp_dst, sizeof(BITMAP), &info_dst);int srcWidth = info_src.bmWidth;int srcHeight = info_src.bmHeight;int dstWidth = info_dst.bmWidth; // 新的图像宽度int dstHeight = info_dst.bmHeight; // 新的图像高度float rateX = (float)(srcWidth - 1 - 0.0001) / (float)(dstWidth - 1);float rateY = (float)(srcHeight - 1 - 0.0001) / (float)(dstHeight - 1);BYTE* bits = (BYTE*)info_src.bmBits;float srcX, srcY;int x0, x1, y0, y1;int x, y;float fL, fR, fT, fB, fTL, fTR, fBL, fBR;for (y = 0; y < dstHeight; y++) {for (x = 0; x < dstWidth; x++) {srcX = (float)x * rateX;srcY = (float)y * rateY;x0 = (int)srcX;y0 = (int)srcY;x1 = x0 + 1;y1 = y0 + 1;BYTE* pTL = &bits[info_src.bmWidthBytes * y0 + (x0 << 2)];BYTE* pTR = &bits[info_src.bmWidthBytes * y0 + (x1 << 2)];BYTE* pBL = &bits[info_src.bmWidthBytes * y1 + (x0 << 2)];BYTE* pBR = &bits[info_src.bmWidthBytes * y1 + (x1 << 2)];BYTE* pTarget = &((BYTE*)info_dst.bmBits)[info_dst.bmWidthBytes * y + (x << 2)];fR = srcX - x0;fB = srcY - y0;fL = (1.0f - fR);fT = (1.0f - fB);fTL = fL * fT;fTR = fR * fT;fBL = fL * fB;fBR = fR * fB;pTarget[0] = (BYTE)(fTL * (float)pTL[0] + fTR * (float)pTR[0] + fBL * (float)pBL[0] + fBR * (float)pBR[0]);pTarget[1] = (BYTE)(fTL * (float)pTL[1] + fTR * (float)pTR[1] + fBL * (float)pBL[1] + fBR * (float)pBR[1]);pTarget[2] = (BYTE)(fTL * (float)pTL[2] + fTR * (float)pTR[2] + fBL * (float)pBL[2] + fBR * (float)pBR[2]);pTarget[3] = (BYTE)(fTL * (float)pTL[3] + fTR * (float)pTR[3] + fBL * (float)pBL[3] + fBR * (float)pBR[3]);}}return true;
}

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

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

相关文章

数据仓库表设计理论

数据仓库表设计理论 数仓顾名思义是数据仓库&#xff0c;其数据来源大多来自于业务数据(例如:关系型数据库)&#xff0c;当设计数仓中表类型时(拉链表、增量表、全量表、流水表、切片表)时&#xff0c;应先观察业务数据的特点再设计数仓表结构 首先业务数据是会不断增长的-即…

练习——动态内存分配的笔试题

今天我们分享几道经典的笔试题&#xff0c;做完直接变成陈泽 第一题 ~~ --------------------------------------------------------------------------------------------------~~ void GetMemory(char* p) {p (char*)malloc(100); } void Test(void) {char* str NULL;Get…

阿里云容器镜像仓库(ACR)的创建和使用

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

使用spark进行hbase的bulkload

使用spark进行hbase的bulkload 一、 背景 HBase 是一个面向列&#xff0c;schemaless&#xff0c;高吞吐&#xff0c;高可靠可水平扩展的 NoSQL 数据库&#xff0c;用户可以通过 HBase client 提供的 put get 等 api 实现在数据的实时读写。在过去的几年里&#xff0c;HBase …

虚拟机 NAT模式联网 重启断网的解决方法

1、虚拟机NAT模式联网 这个博客写的很清楚:按照他的方法可以ping通www.baidu.com 三分钟完成虚拟机联网 小白看了都说好&#xff01;&#xff01;&#xff01; 虚拟机超详细联网教程/步骤 SDN软件定义网络实验_虚拟机联网教程_九号迷妹的博客-CSDN博客 但是虚拟机重启几次之后…

使用arm-none-eabi-gcc编译器搭建STM32的Vscode开发环境

工具 make&#xff1a;Windows中没有make&#xff0c;但是可以通过安装MinGW或者MinGW-w64&#xff0c;得到make。gcc-arm-none-eabi&#xff1a;建议最新版&#xff0c;防止调试报错OpenOCDvscodecubeMX VSCODE 插件 Arm Assembly&#xff1a;汇编文件解析C/C&#xff1a;c…

HTTP 请求走私漏洞(HTTP Request Smuggling)

一、什么是Http 请求走私漏洞&#xff1f; HTTP请求走私漏洞&#xff08;HTTP Request Smuggling&#xff09;是一种安全漏洞&#xff0c;利用了HTTP协议中请求和响应的解析和处理方式的不一致性。攻击者通过构造特定的恶意请求&#xff0c;以欺骗服务器和代理服务器&#xff0…

Godot 4 源码分析 - 增加管道通信

学习研究Godot 4&#xff0c;很爽&#xff0c;虽然很庞杂&#xff0c;但相对于自己的水平来说&#xff0c;很强大&#xff0c;尤其是vulkan这块直接打包可用&#xff0c;省得自己从头琢磨。 一点一点地消化、优化与完善&#xff0c;最终才能成为自己的。 这段时间就在Godot的…

【Matlab】基于随机森林算法的数据分类预测(Excel可直接替换数据)

【Matlab】基于随机森林算法的数据分类预测(Excel可直接替换数据) 1.模型原理2.数学公式3.文件结构4.Excel数据5.分块代码6.完整代码7.运行结果1.模型原理 基于随机森林算法的数据分类预测是一种集成学习方法,用于解决分类问题。它由多个决策树组成,通过对这些决策树的预测…

Spring期刊模板中,引用Appendix中的表格时,页面显示正确但链接跳转错误

错误描述&#xff1a; 正文中有Table1&#xff0c;Table2&#xff0c; Table3. Appendix中有Table A1&#xff0c; Table A2&#xff0c; Table A3 appendix中引用 Table A1&#xff0c;生成pdf后。appendix中显示Table A1&#xff0c;正确&#xff0c;但是点击Table A1以后&…

Pytorch迁移学习使用Resnet50进行模型训练预测猫狗二分类

目录 1.ResNet残差网络 1.1 ResNet定义 1.2 ResNet 几种网络配置 1.3 ResNet50网络结构 1.3.1 前几层卷积和池化 1.3.2 残差块&#xff1a;构建深度残差网络 1.3.3 ResNet主体&#xff1a;堆叠多个残差块 1.4 迁移学习猫狗二分类实战 1.4.1 迁移学习 1.4.2 模型训练 1.…

华为数通HCIP-ISIS基础

IS-IS的基本概念 isis&#xff08;中间系统到中间路由协议&#xff09; 链路状态路由协议、IGP、无类路由协议&#xff1b; IS-IS是一种链路状态路由协议&#xff0c;IS-IS与OSPF在许多方面非常相似:运行IS-IS协议的直连设备之间通过发送Hello报文发现彼此&#xff0c;然后建…

从零开始搭建vue3 + ts + pinia + vite +element-plus项目

前言&#xff1a;据说vue2将于 2023 年 12 月 31 日停止维护&#xff0c;最近打算搭建一个vue3项目来学习一下&#xff0c;以防忘记&#xff0c;记录一下搭建过程。 一、使用npm创建项目 前提条件&#xff1a;已安装 16.0 或更高版本的 Node.js 执行 “npm init vuelatest”…

【Java基础教程】(四十三)多线程篇 · 下:深入剖析Java多线程编程:同步、死锁及经典案例——生产者与消费者,探究sleep()与wait()的差异

Java基础教程之多线程 下 &#x1f539;本节学习目标1️⃣ 线程的同步与死锁1.1 同步问题的引出2.2 synchronized 同步操作2.3 死锁 2️⃣ 多线程经典案例——生产者与消费者&#x1f50d;分析sleep()和wait()的区别&#xff1f; &#x1f33e; 总结 &#x1f539;本节学习目标…

谷歌插件(Chrome扩展) “Service Worker (无效)” 解决方法

问题描述&#xff1a; 写 background 文件的时候报错了&#xff0c;说 Service Worker 设置的 background 无效。 解决&#xff08;检查&#xff09;方法&#xff1a; 检查配置文件&#xff08;manifest.json&#xff09; 中的 manifest_version 是否为 3。 background 中的…

如何动态修改 spring aop 切面信息?让自动日志输出框架更好用

业务背景 很久以前开源了一款 auto-log 自动日志打印框架。 其中对于 spring 项目&#xff0c;默认实现了基于 aop 切面的日志输出。 但是发现一个问题&#xff0c;如果切面定义为全切范围过大&#xff0c;于是 v0.2 版本就是基于注解 AutoLog 实现的。 只有指定注解的类或…

DataWhale AI夏令营——机器学习

DataWhale AI夏令营——机器学习 学习记录一1. 异常值分析2. 单变量箱线图可视化3. 特征重要性分析 学习记录一 锂电池电池生产参数调控及生产温度预测挑战赛 已配置环境&#xff0c;跑通baseline&#xff0c;并在此基础上对数据进行了简单的分析。 1. 异常值分析 对训练集…

Dokuwiki 安装

作者&#xff1a;felix 个人博客 1、拉取镜像 docker pull bitnami/dokuwiki:20200729.0.0-debian-10-r1072、docker-compose部属 docker-compose.yml version: 3 services:dokuwiki:image: docker.io/bitnami/dokuwiki:20200729.0.0-debian-10-r107user: rootports:- 80:80…

K8S初级入门系列之八-网络

一、前言 本章节我们将了解K8S的相关网络概念&#xff0c;包括K8S的网络通讯原理&#xff0c;以及Service以及相关的概念&#xff0c;包括Endpoint&#xff0c;EndpointSlice&#xff0c;Headless service&#xff0c;Ingress等。 二、网络通讯原理和实现 同一K8S集群&…

PMP 数据收集工具与技术

数据收集工具与技术 (9个) 标杆对照 标杆对照是指将实际或计划的产品、流程和实践与其他可比组织的 做法进行比较&#xff0c;以便识别最佳实践、形成改进意见&#xff0c;并为绩效考核 提供依据。 头脑风暴 头脑风暴是一种数据收集和创意技术&#xff0c;主要用于在短时间…