006JPEG图像原理与应用

一、JPEG图像原理与应用

1.基本概念

JPEG(Joint Photographic Experts Group)指的是联合图像专家组,是国际标准化组织ISO制订并于1992年发布的一种面向连续色调静止图像的压缩编码标准,所以也被称为JPEG标准。

同样,JPEG也是一种常用的图像存储格式,JPEG的文件格式有两种文件扩展名:.jpg和.jpeg,这两种扩展名是相同的,我们可以把.jpg的文件改名为.jpeg,而对文件本身不会有任何影响。

img

和其他相同图像质量的文件格式相比,JPEG格式的压缩比是最高的,也就是说在图像质量相差不大的情况下,JPEG文件所占的内存更小。所以JPEG图像一般适合进行网络传输

img

2. 编解码库

由于JPEG格式的文件是经过压缩的,所以用户无法向访问BMP位图一样直接读取JPEG文件中的内容,而是需要使用对应的libjpeg解码库对JPEG文件进行解码,从而得到解码之后的RGB颜色分量,然后把解码之后的RGB颜色分量写入到LCD屏即可。

img

libjpeg是一款开源的JPEG图像库,可以用于编码JPEG文件格式或者用于解码JPEG文件格式,这套库是由IJG(Independent JPEG Group)独立小组进行维护与发布,libjpeg库完全利用C语言设计并且内部集成各种用于编解码算法的函数接口,广泛用于各种图像处理,比如OpenCV(开源的跨平台计算机视觉库)读取图像的底层实现逻辑就是基于libjpeg库的。

img

由于IJG独立小组不属于官方组织,也就是说libjpeg库不属于标准库,而属于第三方库,所以Linux系统并没有直接为用户安装libjpeg库,用户如果打算使用libjpeg库就需要完成库的移植工作。

3.库的移植(重点)

(1) 下载需要移植的库的源码包,libjpeg库源码包在官网可以下载 www.ijg.org,如图所示

image-20260122202650498

(2) 解压压缩包,解压后找到自述文件README,打开README了解libjpeg库的使用规则!

image-20260122202743326

(3) 打开源码包中的install.txt的文本,学习libjpeg库的移植和安装的步骤,移植libjpeg的步骤分为三步:配置(./configure) + 编译(make) + 安装(make install)

image-20260122202815592

(4) 把下载好的源码包jpegsrc.v9f.tar.gz发送到linux系统的家目录下进行解压,注意不可以在共享文件夹进行解压

image-20260122202831368

(5) 切换到解压后的jpeg-9f的文件夹内,然后输入指令配置libjpeg库,*配置*的时候需要使用一个叫做configure的配置文件,该配置文件有两个选项非常重要:--prefix 和 --host

image-20260122202842174

(6) 配置成功之后,会得到一个makefile脚本文件,此时可以完成移植的第二步:*编译*,在命令行输入指令:make ,该指令会自动执行makefile

image-20260122202854574

image-20260122202926413

(7) 编译通过之后,则可以完成libjpeg库的*安装*,此时在命令行输入指令: make install

image-20260122202936073

(8) 安装完成后,可以在用户指定的安装路径中找到生成的libjpeg库的头文件和库文件,此时用户可以选择拷贝出来,就可以设计程序时使用。

image-20260122202946985

(9) 把include文件夹和lib文件夹与自己的工程文件放在同一个路径,方便后期的工程维护!

image-20260122203007265

4.库的使用

image-20260122203053484

image-20260122203115076

image-20260122203126530

为了可以把一张jpg图片显示在LCD上,所以需要把jpg图片进行解压,解压之后就可以得到图片内部的像素点的颜色分量,就可以把像素点的颜色分量向LCD的像素点写入。

5. 解码流程

(1) 创建解码对象,并且对解码对象进行初始化,另外需要创建错误处理对象,并和解码对象进行关联。

image-20260122203206665

(2) 打开待解码的jpg图片,使用fopen的时候需要添加选项”b”,以二进制方式打开文件!

image-20260122203246952

(3) 读取待解码图片的文件头,并把图像信息和解码对象进行关联,通过解码对象对jpg图片进行解码

image-20260122203326292

(4) 可以选择设置解码参数,如果打算以默认参数对jpg图片进行解码,则可以省略该步骤!

image-20260122203340419

(5) 开始对jpg图片进行解码,调用函数之后开始解码,可以得到图像宽、图像高等信息!

image-20260122203352769

(6) *开始设计一个循环,在循环中每次读取1行的图像数据,并写入到LCD中,注意:转换算法需要用户自己设计。*

image-20260122203404311

(7) 在所有的图像数据都已经解码完成后,则调用函数完成解码即可,然后释放相关资源!

image-20260122203414224

image-20260122203423883

6. 程序设计

img

7. 程序编译

由于libjpeg库不是直接安装在Linux系统下,这个libjpeg库是拷贝出来,所以在Linux系统路径下是找不到libjpeg的头文件和库文件的,所以在编译程序的时候需要使用编译器的选项: -I 指定头文件的路径 -L 指定库文件的路径 -l 指定库文件的名称(lib缩写)

img

image-20260122203531676

8.练习

没有错误判断

作业:完成libjpeg库的移植,并设计程序实现在LCD上的任意位置显示一张任意大小的jpg图片,注意不要越界。

#include <stdio.h>
#include <stdlib.h>
#include "jpeglib.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

int *lcd_mp;

int read_JPEG_file (char * filename,int x,int y)
{
  /* This struct contains the JPEG decompression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   */
  struct jpeg_decompress_struct cinfo;
  /* We use our private extension JPEG error handler.
   * Note that this struct must live as long as the main JPEG parameter
   * struct, to avoid dangling-pointer problems.
   */
  struct jpeg_error_mgr jerr;
  /* More stuff */
  FILE * infile;		/* source file */
  unsigned char * buffer;		/* Output row buffer */
  int row_stride;		/* physical row width in output buffer */

  /* In this example we want to open the input file before doing anything else,
   * so that the setjmp() error recovery below can assume the file is open.
   * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
   * requires it in order to read binary files.
   */

  if ((infile = fopen(filename, "rb")) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    return 0;
  }

  /* Step 1: allocate and initialize JPEG decompression object */

  /* We set up the normal JPEG error routines, then override error_exit. */
  cinfo.err = jpeg_std_error(&jerr);
 
  /* Establish the setjmp return context for my_error_exit to use. */
 
  /* Now we can initialize the JPEG decompression object. */
  jpeg_create_decompress(&cinfo);

  /* Step 2: specify data source (eg, a file) */

  jpeg_stdio_src(&cinfo, infile);

  /* Step 3: read file parameters with jpeg_read_header() */

  (void) jpeg_read_header(&cinfo, TRUE);
  /* We can ignore the return value from jpeg_read_header since
   *   (a) suspension is not possible with the stdio data source, and
   *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
   * See libjpeg.txt for more info.
   */

  /* Step 4: set parameters for decompression */

  /* In this example, we don't need to change any of the defaults set by
   * jpeg_read_header(), so we do nothing here.
   */

  /* Step 5: Start decompressor */

  (void) jpeg_start_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* We may need to do some setup of our own at this point before reading
   * the data.  After jpeg_start_decompress() we have the correct scaled
   * output image dimensions available, as well as the output colormap
   * if we asked for color quantization.
   * In this example, we need to make an output work buffer of the right size.
   */ 
  /* JSAMPLEs per row in output buffer */
  row_stride = cinfo.output_width * cinfo.output_components;
  /* Make a one-row-high sample array that will go away when done with image */
  buffer = calloc(1,row_stride);

  /* Step 6: while (scan lines remain to be read) */
  /*           jpeg_read_scanlines(...); */

  /* Here we use the library's state variable cinfo.output_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   */
   int data = 0;
  while (cinfo.output_scanline < cinfo.output_height) {
    
    (void) jpeg_read_scanlines(&cinfo, &buffer, 1);
    for(int i=0;i<cinfo.output_width;i++){
        data |=0XFF<<24;        //A
        data |= buffer[3*i]<<16;  //R
        data |= buffer[3*i+1]<<8; //G
        data |= buffer[3*i+2];    //B
        *(lcd_mp+1024*y+x +(cinfo.output_scanline-1)*1024+i)= data;
        data = 0;
    }
    
  }

  /* Step 7: Finish decompression */

  (void) jpeg_finish_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* Step 8: Release JPEG decompression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_decompress(&cinfo);

  /* After finish_decompress, we can close the input file.
   * Here we postpone it until after no more JPEG errors are possible,
   * so as to simplify the setjmp error logic above.  (Actually, I don't
   * think that jpeg_destroy can do an error exit, but why assume anything...)
   */
  fclose(infile);

  return 1;
}
int main(int argc,char *argv[])
{
    int lcd_fb=open("/dev/fb0",O_RDWR);
	if(-1 ==lcd_fb){
		printf("LCD open error!\n");
		return -1;
	}

	//对LCD内存映射
	lcd_mp=(int *)mmap(NULL,
		1024*600*4,
		PROT_READ | PROT_WRITE,
		MAP_SHARED,
		lcd_fb,
		0
		);
    read_JPEG_file (argv[1],0,0);

    return 0;
}

作业:完成libjpeg库的移植,并设计程序实现把bmp图片转换为jpg图片,学***g图片的压缩算法。

#include <stdio.h>
#include "jpeglib.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>

#pragma pack(1)
struct Bmp_FileHeader{
	char id[2];
	int	size;
	short reserve1;
	short reserve2;
	int offset;
};
struct Bmp_InfoHeader{
	int size;
	int width;
	int height;
	short planes;
	short bit_count;
	int compression;
	int size_image;
	int x_pels_per_meter;
	int y_pels_per_meter;
	int clr_used;
	int clr_important;
};

int image_width=0;
int image_height=0;
char *image_buffer;




void write_JPEG_file (char * filename, int quality)
{
  /* This struct contains the JPEG compression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   * It is possible to have several such structures, representing multiple
   * compression/decompression processes, in existence at once.  We refer
   * to any one struct (and its associated working data) as a "JPEG object".
   */
  struct jpeg_compress_struct cinfo;
  /* This struct represents a JPEG error handler.  It is declared separately
   * because applications often want to supply a specialized error handler
   * (see the second half of this file for an example).  But here we just
   * take the easy way out and use the standard error handler, which will
   * print a message on stderr and call exit() if compression fails.
   * Note that this struct must live as long as the main JPEG parameter
   * struct, to avoid dangling-pointer problems.
   */
  struct jpeg_error_mgr jerr;
  /* More stuff */
  FILE * outfile;		/* target file */
  unsigned char * row_pointer[1];	/* pointer to JSAMPLE row[s] */
  int row_stride;		/* physical row width in image buffer */

  /* Step 1: allocate and initialize JPEG compression object */

  /* We have to set up the error handler first, in case the initialization
   * step fails.  (Unlikely, but it could happen if you are out of memory.)
   * This routine fills in the contents of struct jerr, and returns jerr's
   * address which we place into the link field in cinfo.
   */
  cinfo.err = jpeg_std_error(&jerr);
  /* Now we can initialize the JPEG compression object. */
  jpeg_create_compress(&cinfo);

  /* Step 2: specify data destination (eg, a file) */
  /* Note: steps 2 and 3 can be done in either order. */

  /* Here we use the library-supplied code to send compressed data to a
   * stdio stream.  You can also write your own code to do something else.
   * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
   * requires it in order to write binary files.
   */
  if ((outfile = fopen(filename, "wb")) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    exit(1);
  }
  jpeg_stdio_dest(&cinfo, outfile);

  /* Step 3: set parameters for compression */

  /* First we supply a description of the input image.
   * Four fields of the cinfo struct must be filled in:
   */
  cinfo.image_width = image_width; 	/* image width and height, in pixels */
  cinfo.image_height = image_height;
  cinfo.input_components = 3;		/* # of color components per pixel */
  cinfo.in_color_space = JCS_RGB; 	/* colorspace of input image */
  /* Now use the library's routine to set default compression parameters.
   * (You must set at least cinfo.in_color_space before calling this,
   * since the defaults depend on the source color space.)
   */
  jpeg_set_defaults(&cinfo);
  /* Now you can set any non-default parameters you wish to.
   * Here we just illustrate the use of quality (quantization table) scaling:
   */
  jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);

  /* Step 4: Start compressor */

  /* TRUE ensures that we will write a complete interchange-JPEG file.
   * Pass TRUE unless you are very sure of what you're doing.
   */
  jpeg_start_compress(&cinfo, TRUE);

  /* Step 5: while (scan lines remain to be written) */
  /*           jpeg_write_scanlines(...); */

  /* Here we use the library's state variable cinfo.next_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   * To keep things simple, we pass one scanline per call; you can pass
   * more if you wish, though.
   */
  row_stride = image_width * 3;	/* JSAMPLEs per row in image_buffer */
  //把BGR---RGB   从下到上---从上到下
  char* rgb_buf = (char*)calloc(1,image_width*image_height*3);
  for(int i=0;i<image_height;i++){
  	int bmp_line = image_height-1-i;		//bmp行倒序
  	for(int j=0;j<row_stride;j+=3){
  		rgb_buf[i*row_stride+j]=image_buffer[bmp_line*row_stride+j+2];	//将bmp R ----- 变为jpeg	 R
  		rgb_buf[i*row_stride+j+1]=image_buffer[bmp_line*row_stride+j+1];//将bmp G ----- 变为jpeg	 G
  		rgb_buf[i*row_stride+j+2]=image_buffer[bmp_line*row_stride+j];	//将bmp B ----- 变为jpeg	 B
  	}
  }
  while (cinfo.next_scanline < cinfo.image_height) {
    /* jpeg_write_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could pass
     * more than one scanline at a time if that's more convenient.
     */
    row_pointer[0] = & rgb_buf[cinfo.next_scanline * row_stride];
    (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
  }

  /* Step 6: Finish compression */

  jpeg_finish_compress(&cinfo);
  /* After finish_compress, we can close the output file. */
  fclose(outfile);

  /* Step 7: release JPEG compression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_compress(&cinfo);

  /* And we're done! */
}

int main(int argc,char *argv[])
{
	//打开图片
	FILE * bmp_fd=fopen(argv[1],"rb");
	if (NULL == bmp_fd){
		printf("bmp file open error\n");
		return -1;
	}

	//读取图片的宽和高
	fseek(bmp_fd,14,SEEK_SET);
	struct Bmp_InfoHeader InfoHeader;
	fread(&InfoHeader,1,40,bmp_fd);

	 image_width =	InfoHeader.width;
	 image_height = InfoHeader.height;

	 int buffer_size = image_width*image_height*3;
	 image_buffer=(char *)calloc(1,buffer_size);
	 fread(image_buffer,1,buffer_size,bmp_fd);

	 write_JPEG_file("new.jpeg",100);

}
posted @ 2026-01-22 20:54  郭小胖  阅读(0)  评论(0)    收藏  举报