C++图片格式转换:BMP转JPEG

Wesley13
• 阅读 646

C++方式将BMP格式转换为JPEG格式,依赖了一个第三方库,工程下载链接为:点击打开链接

Bmp2Jpeg.h:

#pragma once

class CBmp2Jpeg
{
public:
    CBmp2Jpeg();
    ~CBmp2Jpeg();

public:
    int Bmp2Jpeg(const char *bmp, const char *jpeg);

private:
    int SaveJpeg(const char *filename, unsigned char *bits, int width, int height, int depth);
    int ReadBmp(const char *bmp, unsigned char **data,int &w, int &h, int &d);
    void Bgra2Rgb(const unsigned char *src, int w, int h, int d, unsigned char *dst);
    void InitFileHeader(void *pFile, void *fileHeader);
    void InitInfoHeader(void *pFile, void *infoHeader);
    void SaveBmp(void *fileHeader, void *infoHeader, int bitCount, unsigned char *data, const char *savename);
private:
    int    m_quality;    //它的大小决定jpg的质量好坏

    enum{
        JPEG_QUALITY = 100, 
    };
};

Bmp2Jpeg.cpp:

#include <iostream>
#include<Windows.h>
#include <fstream>
//#include<stdlib.h>
#include "Bmp2Jpeg.h"
#include <vector>

extern "C"
{
#include "jpeglib.h"
};

#pragma comment(lib,"libjpeg.lib")

using namespace std;

#pragma pack(2)  

struct bmp_fileheader   //文件头,长度为14Byte固定  
{  
    unsigned short bfType;  
    unsigned long bfSize;  
    unsigned short bfReserved1;  
    unsigned short bfReserved2;  
    unsigned long bfOffBits;  
};  

struct bmp_infoheader  //文件信息头,长度为40Byte固定  
{  
    unsigned long biSize;  
    unsigned long biWidth;  
    unsigned long biHeight;  
    unsigned short biPlanes;  
    unsigned short biBitCount;  
    unsigned long biCompression;  
    unsigned long biSizeImage;  
    unsigned long biXPelsPerMeter;  
    unsigned long biYPelsPerMeter;  
    unsigned long biClrUsed;  
    unsigned long biClrImportant;  
}; 

struct RGBPallete
{
    unsigned char b;
    unsigned char g;
    unsigned char r;
    unsigned char alpha;
};

CBmp2Jpeg::CBmp2Jpeg() :
    m_quality(JPEG_QUALITY)
{

}

CBmp2Jpeg::~CBmp2Jpeg()
{

}
/*===================================================================================
function:       jpeg压缩
input:          1:生成的文件名,2:bmp的指针,3:位图宽度,4:位图高度,5:颜色深度
return:         int
description:    bmp的像素格式为(RGB)
===================================================================================*/
int CBmp2Jpeg::SaveJpeg(const char *filename, unsigned char *bits, int width, int height, int depth)
{
    FILE * outfile;                 /* target file */
    fopen_s(&outfile, filename, "wb");
    if (outfile == NULL) {
        return -1;
    }

    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;

    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);

    jpeg_stdio_dest(&cinfo, outfile);

    cinfo.image_width = width;      /* image width and height, in pixels */
    cinfo.image_height = height;
    cinfo.input_components = 3;         /* # of color components per pixel */
    cinfo.in_color_space = JCS_RGB;         /* colorspace of input image */

    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, m_quality, TRUE /* limit to baseline-JPEG values */);

    jpeg_start_compress(&cinfo, TRUE);

    JSAMPROW row_pointer[1];        /* pointer to JSAMPLE row[s] */
    int     row_stride;             /* physical row width in image buffer */
    row_stride = width * depth; /* JSAMPLEs per row in image_buffer */

    while (cinfo.next_scanline < cinfo.image_height) {
        //这里我做过修改,由于jpg文件的图像是倒的,所以改了一下读的顺序
        //row_pointer[0] = & bits[cinfo.next_scanline * row_stride];
        row_pointer[0] = & bits[(cinfo.image_height - cinfo.next_scanline - 1) * row_stride];
        (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }

    jpeg_finish_compress(&cinfo);
    fclose(outfile);

    jpeg_destroy_compress(&cinfo);
    return 0;
}

void CBmp2Jpeg::InitFileHeader(void *pFile, void *fileHeader)
{
    bmp_fileheader *pfileHeader = (bmp_fileheader *)fileHeader;
    fstream *filein = (fstream *)pFile;
    //初始化文件头
    pfileHeader->bfType = 0;
    pfileHeader->bfSize=0;
    pfileHeader->bfReserved1=0;
    pfileHeader->bfReserved2=0;
    pfileHeader->bfOffBits=0;
    //读位图文件头并输出相应信息
    filein->read((char*)fileHeader, sizeof(bmp_fileheader));
}

void CBmp2Jpeg::InitInfoHeader(void *pFile, void *infoHeader)
{
    bmp_infoheader *pinfoHeader = (bmp_infoheader *)infoHeader;
    fstream *filein = (fstream *)pFile;
    //初始化信息头
    pinfoHeader->biSize=0;
    pinfoHeader->biWidth=0;
    pinfoHeader->biHeight=0;
    pinfoHeader->biPlanes=0;
    pinfoHeader->biBitCount=0;
    pinfoHeader->biCompression=0;
    pinfoHeader->biSizeImage=0;
    pinfoHeader->biXPelsPerMeter=0;
    pinfoHeader->biYPelsPerMeter=0;
    pinfoHeader->biClrUsed=0;
    pinfoHeader->biClrImportant=0;

    //读位图信息头并输出相应信息
    filein->read((char*)infoHeader, sizeof(bmp_infoheader));
}

void CBmp2Jpeg::SaveBmp(void *fileHeader, void *infoHeader, int bitCount, unsigned char *data, const char *savename)
{
    bmp_fileheader *pfileHeader = (bmp_fileheader *)fileHeader;
    bmp_infoheader *pinfoHeader = (bmp_infoheader *)infoHeader;
    //写入文件
    std::string str(savename);
    str.append(".bmp");
    fstream fileout;
    fileout.open(str, std::ios::binary | std::ios::out);
    fileout.write((char*)fileHeader, sizeof(bmp_fileheader));
    fileout.write((char*)infoHeader, sizeof(bmp_infoheader));
    fileout.write((char*)data, sizeof(unsigned char) * pfileHeader->bfSize - pfileHeader->bfOffBits);
    
    fileout.close();
}

//读取并将图片另存为一个新文件, 转换成rgb格式
int CBmp2Jpeg::ReadBmp(const char *bmp, unsigned char **data, int &w, int &h, int &d)
{
    //打开位图文件
    fstream filein;
    filein.open(bmp, std::ios::binary | std::ios::in);
    if(!filein.is_open())
    {
        char clog[256] = {0};
        sprintf_s(clog, sizeof(clog), "bmp转jpeg,找不到 %s\n", bmp);
        OutputDebugStringA(clog);
        return -1;
    }

    //定义变量
    long width=0;
    long height=0;
    long bitCount=0;

    bmp_fileheader  fileHeader;
    bmp_infoheader  infoHeader;

    InitFileHeader(&filein, &fileHeader);

    if (fileHeader.bfType != 0x4d42)
    {
        filein.close();
        return -1;
    }

    InitInfoHeader(&filein, &infoHeader);

    width=infoHeader.biWidth;
    height=infoHeader.biHeight;
    bitCount=infoHeader.biBitCount;
    
    int bitPerLine= ((width * bitCount + 31)  >> 5) << 2;
    int imgSize = abs(height * bitPerLine);
    int imgReal = fileHeader.bfSize - fileHeader.bfOffBits;
    if(imgSize != imgReal)
    {
        char clog[256] = {0};
        sprintf_s(clog, sizeof(clog), "bmp转jpeg,图像尺寸不对\n");
        OutputDebugStringA(clog);
        filein.close();
        return -1;
    }

    if (bitCount == 8)
    {
        std::vector<RGBPallete> palletes;
        unsigned char buf[256 * sizeof(RGBPallete)];

        filein.read((char*)buf, 256 * sizeof(RGBPallete));

        for (int i = 0; i < 256; i++)
        {
            RGBPallete pallete;
            memcpy(&pallete, buf + i * sizeof(RGBPallete), sizeof(RGBPallete));

            palletes.push_back(pallete);
        }

        unsigned char* pTemp = new unsigned char[imgSize];
        filein.read((char*)pTemp, imgSize);

        *data = new unsigned char[width * abs(height) * 4];
        for (int i = 0; i < imgSize; i++)
        {
            RGBPallete& p = palletes[pTemp[i]];
            memcpy((*data) + i * sizeof(RGBPallete), &p, sizeof(RGBPallete));
        }

        bitCount = 32;
        delete pTemp;
    }
    else if (bitCount == 24 || bitCount == 32)
    {
        *data = new unsigned char[imgSize];
        filein.read((char*)(*data), imgSize);
        filein.close();
    }
    else
    {
        filein.close();
        return -1;
    }

    w = width;
    h = height;
    d = bitCount;
    
    return 0;
} 

void CBmp2Jpeg::Bgra2Rgb(const unsigned char *src, int w, int h, int d, unsigned char *dst)
{
    unsigned char* pTempDst = dst;
    for (int i = 0; i < abs(h); i++)
    {
        const unsigned char* pTempSrc = nullptr;
        if (h > 0)
        {
            pTempSrc = src + w * i * d;
        }
        else
        {
            pTempSrc = src + w * abs( i  + h + 1 ) * d;
        }

        for (int j = 0; j < w; j++)
        {
            *(pTempDst) = *(pTempSrc + 2);
            *(pTempDst + 1) = *(pTempSrc + 1);
            *(pTempDst + 2) = *(pTempSrc);
            pTempDst += 3;
            pTempSrc += d;
        }
    }
}


int CBmp2Jpeg::Bmp2Jpeg(const char *bmp, const char *jpeg)
{
    unsigned char *brga = nullptr; //指向位图buffer的全局指针,window下像素格式: BGRA(4个字节)
    int width = 0, height = 0, depth = 0;

    if(ReadBmp(bmp, &brga,width, height, depth) < 0)
    {
        return -1;
    }

    unsigned char *rgb = new unsigned char[width * abs(height) * depth/8];
    Bgra2Rgb(brga, width, height, depth/8, rgb);

    int ret = SaveJpeg(jpeg, rgb, width, abs(height), 3);

    delete [] brga;
    delete [] rgb;
    brga = nullptr;
    rgb = nullptr;
    return ret;
}

调用:

#include <iostream>
#include <stdio.h>
#include "Bmp2Jpeg.h"
using namespace std;
int main()
{
    CBmp2Jpeg bmp;
    bmp.Bmp2Jpeg("111_24.bmp", "lena.jpg");
    cout<<"good job."<<endl;
    cin.get();
    return 0;
}
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Stella981 Stella981
3年前
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解2016年09月02日00:00:36 \牧野(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fme.csdn.net%2Fdcrmg) 阅读数:59593
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Stella981 Stella981
3年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
3年前
Google地球出现“无法连接到登录服务器(错误代码:c00a0194)”解决方法
Google地球出现“无法连接到登录服务器(错误代码:c00a0194)”解决方法参考文章:(1)Google地球出现“无法连接到登录服务器(错误代码:c00a0194)”解决方法(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.codeprj.com%2Fblo
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这