图片压缩处理(一)

2025博客之星年度评选已开启 10w+人浏览 1.5k人参与

压缩项目是仅用于Windows平台的,主要使用gdi+方案,如果需要跨平台建议用opencv。本项目目前处理图片的类型只有jpg和png。

首先是jpg方面,jpg图片没有透明通道

png图片可能有透明通道,有函数可以检查这方面的信息,本项目简化了这一部分操作,统一用24rgb参数压缩(8r8g8b),jpg的缩放质量参数是0-100,计算好后新宽高注意要/100。png没有缩放质量参数。压缩使用的是高质量双三次插值模式(InterpolationModeHighQualityBicubic),每个像素点根据周围4*4共计16个像素点进行插值计算。

图片压缩函数

static bool compressImage(std::string_view input_path, const std::string_view output_path, int quality,
                              IMG_TYPE type, int scale) {
        using namespace Gdiplus;
        spdlog::debug("Compress image: {}--{}", input_path, output_path);

        // 初始化GDI+
        GdiplusStartupInput const gdiplus_startup_input{};
        ULONG_PTR gdiplus_token = 0;
        GdiplusStartup(&gdiplus_token, &gdiplus_startup_input, nullptr);

        Image* image = Image::FromFile(CommonTool::charToWchar(input_path).c_str());
        if (image == nullptr || image->GetLastStatus() != Ok) {
            GdiplusShutdown(gdiplus_token);
            return false;
        }

        UINT const original_width = image->GetWidth();
        UINT const original_height = image->GetHeight();
        INT const new_width = static_cast<INT>(original_width * scale) / 100;
        INT const new_height = static_cast<INT>(original_height * scale) / 100;

        auto* scaled_bitmap = new Bitmap(new_width, new_height, PixelFormat24bppRGB);
        Graphics* graphics = Graphics::FromImage(scaled_bitmap);
        graphics->SetInterpolationMode(InterpolationModeHighQualityBicubic);
        graphics->DrawImage(image, 0, 0, new_width, new_height);

        CLSID encoder_clsid;
        if (type == IT_PNG) {
            getEncoderClsid(L"image/png", &encoder_clsid);
        } else {
            getEncoderClsid(L"image/jpeg", &encoder_clsid);
        }

        // 设置编码参数
        EncoderParameters encoder_parameters{};
        encoder_parameters.Count = 1;
        encoder_parameters.Parameter[0].Guid = EncoderQuality;
        encoder_parameters.Parameter[0].Type = EncoderParameterValueTypeLong;
        encoder_parameters.Parameter[0].NumberOfValues = 1;

        // JPEG质量范围是0-100,PNG不支持质量设置
        ULONG quality_value = (type == IT_PNG) ? 100 : quality;
        encoder_parameters.Parameter[0].Value = &quality_value;

        auto result =
            scaled_bitmap->Save(CommonTool::charToWchar(output_path).c_str(), &encoder_clsid, &encoder_parameters);

        delete image;
        delete scaled_bitmap;
        delete graphics;
        GdiplusShutdown(gdiplus_token);
        return result;
    }

日志记录 → 初始化GDI+ → 加载图片 → 计算缩放 → 创建Bitmap → 绘制缩放 → 获取编码器 → 设置参数 → 保存图片 → 释放资源 → 返回结果

生成指定文件大小的图片函数

有些时候压缩需要考虑到对压缩后文件大小有要求的情况,这个函数的功能就是尝试修改质量参数来使最终的文件大小尽可能接近目标大小。

主要功能和流程如下:

  1. 初始化 GDI+
    启动 GDI+ 环境,准备图片处理。

  2. 加载图片
    把输入路径的图片加载为 GDI+ 的 Image 对象。

  3. 选择编码器
    根据目标类型(PNG/JPG)选择合适的图片编码器。

  4. 目标大小转换
    把目标大小(KB)转换为字节(乘以 1024)。

  5. 初始质量设置
    根据目标大小,初步设定压缩质量(小于100KB用30,500KB用50,1MB用70,否则用90)。

  6. 二分查找压缩质量
    最多尝试10次,通过调整压缩质量,反复保存临时图片并检测文件大小,直到接近目标大小。

  7. 保存最终图片
    找到合适质量后,把临时文件移动为最终输出文件。

  8. 清理资源
    释放图片对象和 GDI+ 资源,删除临时文件。

static bool compressImageBySize(const std::string& input_path, const std::string& output_path, int target_size,
                                    IMG_TYPE type) {
        using namespace Gdiplus;

        // 初始化GDI+
        GdiplusStartupInput gdiplusStartupInput;
        ULONG_PTR gdiplusToken;
        GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);

        // 加载图像
        auto* wideInputPath = new wchar_t[input_path.length() + 1];
        size_t convertedChars = 0;
        mbstowcs_s(&convertedChars, wideInputPath, input_path.length() + 1, input_path.c_str(), _TRUNCATE);

        Image* image = Image::FromFile(wideInputPath);
        if (image == nullptr || image->GetLastStatus() != Ok) {
            delete[] wideInputPath;
            GdiplusShutdown(gdiplusToken);
            return false;
        }

        delete[] wideInputPath;

        // 获取编码器CLSID
        CLSID encoderClsid;
        if (type == IT_JPG) {
            getEncoderClsid(L"image/jpeg", &encoderClsid);
        } else if (type == IT_PNG) {
            getEncoderClsid(L"image/png", &encoderClsid);
        } else {
            // 默认使用JPEG格式
            getEncoderClsid(L"image/jpeg", &encoderClsid);
            type = IT_JPG;
        }

        target_size *= 1024;
        uint32_t quality = 90;
        if (target_size < static_cast<uint64_t>(100 * 1024)) {  // 小于100KB
            quality = 30;
        } else if (target_size < static_cast<uint64_t>(500 * 1024)) {  // 小于500KB
            quality = 50;
        } else if (target_size < static_cast<long long>(1024) * 1024) {  // 小于1MB
            quality = 70;
        }

        // 通过二分查找找到合适的质量值
        uint32_t min_quality = 1;
        uint32_t max_quality = 100;

        // 创建临时文件路径用于测试
        std::string const temp_path = output_path + ".tmp";

        for (int i = 0; i < 10; i++) {  // 最多尝试10次
            // 设置编码参数
            EncoderParameters encoderParameters{};
            encoderParameters.Count = 1;
            encoderParameters.Parameter[0].Guid = EncoderQuality;
            encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
            encoderParameters.Parameter[0].NumberOfValues = 1;

            ULONG qualityValue = (type == IT_PNG) ? 100 : quality;
            encoderParameters.Parameter[0].Value = &qualityValue;

            // 保存图像到临时文件
            Status status = image->Save(CommonTool::charToWchar(temp_path).c_str(), &encoderClsid, &encoderParameters);

            if (status != Ok) {
                // 保存失败,降低质量再试
                max_quality = quality - 1;
                quality = (min_quality + max_quality) / 2;
                continue;
            }

            // 检查文件大小
            WIN32_FILE_ATTRIBUTE_DATA file_data;
            if (GetFileAttributesExW(CommonTool::charToWchar(temp_path).c_str(), GetFileExInfoStandard, &file_data)) {
                ULARGE_INTEGER file_size;
                file_size.LowPart = file_data.nFileSizeLow;
                file_size.HighPart = file_data.nFileSizeHigh;

                if (file_size.QuadPart <= target_size) {
                    std::string path = "\"";
                    path += output_path;
                    path += "\"";

                    MoveFileW(CommonTool::charToWchar(temp_path).c_str(), CommonTool::charToWchar(path).c_str());

                } else {
                    // 文件太大,降低质量
                    max_quality = quality - 1;
                }
            } else {
                max_quality = quality - 1;
            }

            // 调整质量值
            if (max_quality < min_quality) {
                // 已经是最低质量,保存并退出
                auto* wide_temp_path_for_final = new wchar_t[temp_path.length() + 1];
                mbstowcs_s(&convertedChars, wide_temp_path_for_final, temp_path.length() + 1, temp_path.c_str(),
                           _TRUNCATE);

                auto* wideOutputPathForFinal = new wchar_t[output_path.length() + 1];
                mbstowcs_s(&convertedChars, wideOutputPathForFinal, output_path.length() + 1, output_path.c_str(),
                           _TRUNCATE);

                MoveFileW(wide_temp_path_for_final, wideOutputPathForFinal);
                delete[] wide_temp_path_for_final;
                delete[] wideOutputPathForFinal;
                break;
            }

            quality = (min_quality + max_quality) / 2;
        }

        // 清理资源
        delete image;
        GdiplusShutdown(gdiplusToken);

        // 删除临时文件(如果存在)
        DeleteFileA(temp_path.c_str());
        return true;
    }

基于径向基函数神经网络RBFNN的自适应滑模控制学习(Matlab代码实现)内容概要:本文介绍了基于径向基函数神经网络(RBFNN)的自适应滑模控制方法,并提供了相应的Matlab代码实现。该方法结合了RBF神经网络的非线性逼近能力和滑模控制的强鲁棒性,用于解决复杂系统的控制问题,尤其适用于存在不确定性和外部干扰的动态系统。文中详细阐述了控制算法的设计思路、RBFNN的结构与权重更新机制、滑模面的构建以及自适应律的推导过程,并通过Matlab仿真验证了所提方法的有效性和稳定性。此外,文档还列举了大量相关的科研方向和技术应用,涵盖智能优化算法、机器学习、电力系统、路径规划等多个领域,展示了该技术的广泛应用前景。; 适合人群:具备定自动控制理论基础和Matlab编程能力的研究生、科研人员及工程技术人员,特别是从事智能控制、非线性系统控制及相关领域的研究人员; 使用场景及目标:①学习和掌握RBF神经网络与滑模控制相结合的自适应控制策略设计方法;②应用于电机控制、机器人轨迹跟踪、电力电子系统等存在模型不确定性或外界扰动的实际控制系统中,提升控制精度与鲁棒性; 阅读建议:建议读者结合提供的Matlab代码进行仿真实践,深入理解算法实现细节,同时可参考文中提及的相关技术方向拓展研究思路,注重理论分析与仿真验证相结合。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值