最近用的攝像頭OV2640攝像頭,支持RGB565、JPEG、YUV和YCbCr格式的輸出,想要對採集的圖像進行色塊識別,這樣一般需要用到RGB格式的輸出會比較好處理,數字圖像處理中常用的採用模型是RGB(紅,綠,藍)模型和HSV(色調,飽和度,亮度),RGB廣泛應用於彩色監視器和彩色視頻攝像機,我們平時的圖片一般都是RGB模型。別人是RGB的LCD屏幕作爲上位機,而我卻沒有LCD屏幕作爲上位機,輸出JPEG我可以存爲.jpg爲後綴的文件,輸出RGB565的數據不好將圖片保存下來,存RGB格式一般存爲bmp位圖,就想着既然板子都是作爲客戶端採集圖像傳出來,在服務器端將JPEG的數據轉換成RGB數據在做色塊識別的處理,就瞭解到libjpeg這個庫,libjpeg是一款功能強大的開源jpeg圖像庫工具軟件,jpeg是一個國際圖像壓縮標準,圖像的後綴一般爲jpeg或者jpg,利用libjpeg可以讀取jpeg圖像數據,libjpeg庫是用於編碼數據爲jpeg格式或者解碼jpeg格式圖片的常用庫。
既然傳出來的數據是JPEG,這裏需要做的就是JPEG的解碼,下面簡單的說說libjpeg庫的使用:
一、libjpeg庫的簡單使用
1、設置出錯處理函數並初始化編解碼結構對象
在libjpeg庫中,實現了默認錯誤處理函數,當錯誤發生時,比如如果內存不足(非常可能發生,後面會介紹)等,則默認錯誤處理函數將會調用exit函數結束整個進程,詳細內容可以參考jerror.c文件。在C語言中沒有C++的異常處理機制,但是提供了setjmp和longjmp機制來實現類似的功能,設置完出錯處理就初始化編解碼結構對象
編碼:
/* Step 1: allocate and initialize JPEG compression object */
cinfo.err = jpeg_std_error(&jerr);
/* Now we can initialize the JPEG compression object. */
jpeg_create_compress(&cinfo);
解碼:
/* We set up the normal JPEG error routines, then override error_exit. */
cinfo.err = jpeg_std_error(&jerr);
/* Now we can initialize the JPEG decompression object. */
jpeg_create_decompress(&cinfo);
2、初始化源數據
在libjpeg庫中僅僅提供了文件作爲輸入數據的接口,在example.c中代碼如下:jpeg_stdio_src(&cinfo, infile);
3、讀取jpeg文件的頭信息
調用jpeg_read_header(&cinfo, TRUE)讀取jpeg文件頭信息,這個和初始化解碼對象一樣,是必須要調用的,是約定
(void) jpeg_read_header(&cinfo, TRUE);
/* 源信息 */
printf("image_width = %d\n", cinfo.image_width);
printf("image_height = %d\n", cinfo.image_height);
printf("num_components = %d\n", cinfo.num_components);
4、設置解碼參數
很多情況下,這步非常重要。比如設置輸出格式,設置scale(縮放)等等功能都是在這一步設置。參數設置通過修改上步得到cinfo的值來實現。這裏簡單介紹一下一些常用的字段。out_color_space:輸出的顏色格式,libjpeg定義如下:
typedef enum {
JCS_UNKNOWN, /* error/unspecified */
JCS_GRAYSCALE, /* monochrome */
JCS_RGB, /* red/green/blue */
JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */
JCS_CMYK, /* C/M/Y/K */
JCS_YCCK, /* Y/Cb/Cr/K */
} J_COLOR_SPACE;
不設置的話通常就是JCS_RGB,RGB888的格式,除此之外,還可以設置縮放大小等,scale_num,scale_denom:因爲實際的顯示設備千變萬化,我們可能需要根據實際情況對輸出數據進行一些縮放才能夠顯示。libjpeg支持對輸出數據進行縮放(scale),這個變量就是用來設置縮放的參數。
mem:可以指定內存管理相關的內容,比如分配和釋放內存,指定libjpeg可以使用的最大內存。默認情況下不同的平臺下面都有一個libjpeg默認最大可用內存值,比如Android平臺上面該值爲10000000L(10M),請參考jmemxxxx.c文件中的DEFAULT_MAX_MEM,瞭解不同平臺的默認最大內存值。通過修改mem->pub.max_memory_to_use的值,庫的使用者可以自定義libjpeg可以使用的最大內存值。
5、開始解碼
經過前面的參數設置,接着調用jpeg_start_decompress(&cinfo);開始解碼。
6、對解碼出來的數據做處理
/* Step 6: while (scan lines remain to be read) */
/* jpeg_read_scanlines(...); */
while (cinfo.output_scanline < cinfo.output_height)
{
row_pointer[0] = & rdata[(cinfo.output_scanline)*cinfo.image_width*cinfo.num_components];
jpeg_read_scanlines(&cinfo,row_pointer ,1);
//(void) jpeg_read_scanlines(&cinfo, buffer, 1);
/* Assume put_scanline_someplace wants a pointer and sample count. */
//put_scanline_someplace(buffer[0], row_stride);
}
解碼出來的數據是一行一行的,我這裏讀到rdata裏面,這裏根據個人需求改。
解碼JPEG文件輸出RGB888數據到out_buf
int read_JPEG_file (char * filename, unsigned char *out_buf,int size)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
/* More stuff */
FILE *infile; /* source file */
int row_stride; /* physical row width in output buffer */
unsigned char *rdata;
JSAMPROW row_pointer[1];
int image_size = 0 ;
if ((infile = fopen(filename, "rb")) == NULL)
{
fprintf(stderr, "can't open %s\n", filename);
return -1;
}
/* 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);
/* 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);
/* 源信息 */
printf("image_width = %d\n", cinfo.image_width);
printf("image_height = %d\n", cinfo.image_height);
printf("num_components = %d\n", cinfo.num_components);
image_size = cinfo.image_width*cinfo.image_height*cinfo.num_components ;
//傳進來的字符數組沒有足夠大的空間
if(size < image_size )
{
printf("buf size too small!\n") ;
return -2 ;
}
/* 分配內存存儲字節 */
rdata=(unsigned char*)calloc(image_size,1);
/* Step 4: set parameters for decompression */
//設置輸出的顏色類型
cinfo.out_color_space=JCS_RGB;
/* Step 5: Start decompressor */
(void) jpeg_start_decompress(&cinfo);
/* 輸出的圖象的信息 */
printf("output_width = %d\n", cinfo.output_width);
printf("output_height = %d\n", cinfo.output_height);
printf("output_components = %d\n", cinfo.output_components);//解壓的是rgb,故爲3元素
/* 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 */
/* Step 6: while (scan lines remain to be read) */
/* jpeg_read_scanlines(...); */
while (cinfo.output_scanline < cinfo.output_height)
{
row_pointer[0] = & rdata[(cinfo.output_scanline)*cinfo.image_width*cinfo.num_components];
jpeg_read_scanlines(&cinfo,row_pointer ,1);
//(void) jpeg_read_scanlines(&cinfo, buffer, 1);
/* Assume put_scanline_someplace wants a pointer and sample count. */
//put_scanline_someplace(buffer[0], row_stride);
}
memcpy(out_buf, rdata, image_size) ;
free(rdata) ;
(void) jpeg_finish_decompress(&cinfo);
/* Step 8: Release JPEG decompression object */
/* This is an important step since it will release a good deal of memory. */
jpeg_destroy_decompress(&cinfo);
fclose(infile);
/* And we're done! */
return image_size;
}
將RGB888數據編碼保存生成文件
int write_JPEG_file (char * filename,unsigned char *image_buffer, int image_width, int image_height, int quality )
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
/* More stuff */
FILE * outfile; /* target file */
JSAMPROW 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 */
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. */
if ((outfile = fopen(filename, "w+")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
return -1;
}
jpeg_stdio_dest(&cinfo, outfile);
/* Step 3: set parameters for compression */
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 */
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
/* Step 4: Start compressor */
jpeg_start_compress(&cinfo, TRUE);
/* Step 5: while (scan lines remain to be written) */
row_stride = image_width * 3; /* JSAMPLEs per row in image_buffer */
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = & image_buffer[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! */
return 0 ;
}
參考:https://www.iteye.com/blog/canlynet-1433259
二、RGB888與RGB565互相轉換
在我們的計算機中圖像是以RGB888格式顯示圖像的,24位圖每個像素保存了32bit的數據,即RGB888+Alpha,Alpha就是半透明填充字節……但是對於真彩的圖像而言,肉眼在16bit的時候已經難以分辨了,因此,有些時候,可以講RGB888轉換爲RGB565來存儲,減少了存儲器的容量的同時,降低了數據量
1、RGB888->RGB565
方法只要提取相應單色高位即可(R5 G6 B5),但會導致低位的缺失,影響精度,而且無法恢復。
2、RGB565->RGB888
方法只要補充相應單色低位即可(R3 G2 B3)。
這裏做的就是簡單的轉換,就會有精度的丟失,如果要求精度可能就要做量化補償
RGB888用unsigned int 32位字節存儲
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
R7 |
R6 |
R5 |
R4 |
R3 |
R2 |
R1 |
R0 |
G7 |
G6 |
G5 |
G4 |
G3 |
G2 |
G1 |
G0 |
B7 |
B6 |
B5 |
B4 |
B3 |
B2 |
B1 |
B0 |
RGB565用unsigned short 16位字節存儲
R7 |
R6 |
R5 |
R4 |
R3 |
G7 |
G6 |
G5 |
G4 |
G3 |
G2 |
B7 |
B6 |
B5 |
B4 |
B3 |
rgb888轉rgb565
/* *
* RGB888用unsigned int 32位字節存儲:
* 0 0 0 0 0 0 R7 R6 R5 R4 R3 R2 R1 R0 G7 G6 G5 G4 G3 G2 G1 G0 B7 B6 B5 B4 B3 B2 B1 B0
* RGB565用unsigned short 16位字節存儲
* R7 R6 R5 R4 R3 G7 G6 G5 G4 G3 G2 B7 B6 B5 B4 B3
* */
int rgb888torgb565(unsigned char* rgb888_buf, int rgb888_size, unsigned short *rgb565_buf, int rgb565_size)
{
int i = 0 ;
unsigned char Red = 0 ;
unsigned char Green = 0 ;
unsigned char Blue = 0 ;
int count = 0 ;
if(rgb888_buf == NULL || rgb888_size <= 0 || rgb565_buf == NULL || rgb565_size <= 0 || (rgb565_size < (rgb888_size/3)*2 ) )
{
printf("Invail input parameter in %s\n", __FUNCTION__) ;
return -1 ;
}
for(i = 0; i<rgb888_size; i += 3)
{
Red = rgb888_buf[i] >> 3;
Green = rgb888_buf[i+1] >> 2 ;
Blue = rgb888_buf[i+2] >> 3;
rgb565_buf[count++] = ( (Red<<11)|(Green<<5)|(Blue) ) ;
}
return count ;
}
RGB565轉RGB888
int rgb565torgb888(unsigned short *rgb565_buf, int rgb565_size,unsigned char*rgb888_buf, int rgb888_size)
{
int i = 0 ;
unsigned char Red = 0 ;
unsigned char Green = 0 ;
unsigned char Blue = 0 ;
int count = 0 ;
if(rgb565_buf == NULL || rgb565_size <= 0|| rgb888_buf == NULL || rgb888_size <= 0 )
{
printf("Invaild input parameter in %s\n",__FUNCTION__) ;
return -1 ;
}
for(i=0; i<rgb565_size; i++)
{
// 獲取RGB單色,並填充低位
Red = (rgb565_buf[i] & RGB565_RED) >> 8 ;
Green = (rgb565_buf[i] & RGB565_GREEN) >> 3 ;
Blue = (rgb565_buf[i] & RGB565_BLUE) << 3 ;
// 連接
rgb888_buf[count++] = Red ;
rgb888_buf[count++] = Green ;
rgb888_buf[count++] = Blue ;
}
return count ;
}
參考:https://blog.csdn.net/oxunfeng/article/details/9635513
三、色塊識別
最後就是色塊的識別,這裏是從STM32代碼搬過來用的
兩個字節表示一個RGB圖像像素
/**************************************************
尋找色塊參數
RED_MIN 紅色分量最小值
RED_MAX 紅色分量最大值
GREEN_MIN 綠色分量最小值
GREEN_MAX綠色分量最大值
BLUE_MIN 藍色分量最小值
BLUE_MAX 藍色分量最大值
Ball_W_MIN£ 球寬度最小值
Ball_H_MIN 球高度最小值
Ball_W_MAX 球寬度最大值
Ball_H_MAX 球高度最大值
**************************************************/
int BALL_RED_MIN=16;
int BALL_RED_MAX=31;
int BALL_GREEN_MIN=0;
int BALL_GREEN_MAX=25;
int BALL_BLUE_MIN=0;
int BALL_BLUE_MAX=18;
//小球的大小
int Ball_W_MIN=5;
int Ball_H_MIN=5;
int Ball_W_MAX=100;
int Ball_H_MAX=100;
/******************
匹配小球色塊
******************/
unsigned char Ball_Match_Color(unsigned short colorRGB565)
{
unsigned char R,G,B;//拆分顏色
R=(colorRGB565>>11)&0x1f;//右移11位取低五位爲R
G=(colorRGB565>>5)&0x3f;//右移5位後取六位爲G
B=colorRGB565&0x1f;//取第五位爲B
if((R>=BALL_RED_MIN)&&(R<=BALL_RED_MAX) && (G>=BALL_GREEN_MIN)&&(G<=BALL_GREEN_MAX) && (B>=BALL_BLUE_MIN)&&(B<=BALL_BLUE_MAX))
return 1;//符合這個球最小顏色分量以及大小返回1
else
return 0;
}
/****************************
找色塊中心大小
******************************/
unsigned char Find_Center_Size(unsigned short *p , unsigned char valid_x , unsigned char valid_y , unsigned char *sx , unsigned char *ex , unsigned char *sy , unsigned char *ey)
{
unsigned char repeat_num=2;//中心尋找重複次數
unsigned char step=1;//跨越步數
float error_endure=0.3f;//可容忍誤差比例
unsigned char error_endure_num=3;
unsigned char find_num=0,error_num=0;//通過個數和出錯個數
unsigned char x_min=0,x_max=0;
unsigned char y_min=0,y_max=0;
unsigned char centre_x=0,centre_y=0;//中心點
centre_x=valid_x;
centre_y=valid_y;
while(repeat_num--)//重複尋找中心
{
while(1)//X減小方向尋找邊界
{
find_num+=step;
if((centre_x-find_num)<0)//超邊界,跳出循環
{
x_min=0;//x_min記錄
find_num=0;
error_num=0;
break;
}
if(!Ball_Match_Color(p[centre_y*camera_W+centre_x-find_num]))//不匹配點
error_num++;//不匹配點計數
//超容忍誤差,跳出循環
if(error_num>error_endure_num && ((float)error_num/(float)find_num)>error_endure)
{
x_min=centre_x-find_num;//x_min記錄
find_num=0;
error_num=0;
break;
}
}
while(1)//X增加方向尋找邊界
{
find_num+=step;
if((centre_x+find_num)>=camera_W)//超邊界,跳出循環
{
x_max=camera_W-1;//x_max記錄
find_num=0;
error_num=0;
break;
}
if(!Ball_Match_Color(p[centre_y*camera_W+centre_x+find_num]))//不匹配點
error_num++;//不匹配點計數
//超出誤差容忍範圍,跳出循環
if(error_num>error_endure_num && ((float)error_num/(float)find_num)>error_endure)
{
x_max=centre_x+find_num;//x_max記錄
find_num=0;
error_num=0;
break;
}
}
while(1)//Y減小方向尋找邊界
{
find_num+=step;
if((centre_y-find_num)<0)//超出邊界,跳出循環
{
y_min=0;//y_min記錄
find_num=0;
error_num=0;
break;
}
if(!Ball_Match_Color(p[(centre_y-find_num)*camera_W+centre_x]))//不匹配點計數
error_num++;
//超出誤差容忍範圍,跳出循環
if(error_num>error_endure_num && ((float)error_num/(float)find_num)>error_endure)
{
y_min=centre_y-find_num;//y_min記錄
find_num=0;
error_num=0;
break;
}
}
while(1)//Y增加方向尋找邊界
{
find_num+=step;
if((centre_y+find_num)>=camera_H)//超出邊界,跳出循環
{
y_max=camera_H-1;//y_max記錄
find_num=0;
error_num=0;
break;
}
if(!Ball_Match_Color(p[(centre_y+find_num)*camera_W+centre_x]))//不匹配點計數
error_num++;
//超出誤差容忍範圍,跳出循環
if(error_num>error_endure_num && ((float)error_num/(float)find_num)>error_endure)
{
y_max=centre_y+find_num;//y_max記錄
find_num=0;
error_num=0;
break;
}
}
//四個方向找完計算中心點位置
centre_x=(x_min+x_max)/2;
centre_y=(y_min+y_max)/2;
}
if((x_max-x_min)>Ball_W_MIN && (x_max-x_min)<Ball_W_MAX && (y_max-y_min)>Ball_H_MIN && (y_max-y_min)<Ball_H_MAX )//大小匹配
{
*sx=x_min;
*ex=x_max;
*sy=y_min;
*ey=y_max;
return 1;//大小匹配成功
}
else
{
return 0;//大小匹配失敗
}
}
/***************************
在一幀RGB圖像中尋找色塊
***************************/
unsigned char Find_Ball(unsigned short *p , unsigned char start_x , unsigned char start_y , unsigned char *sx , unsigned char *ex , unsigned char *sy , unsigned char *ey)
{
unsigned char step=2;
unsigned char ball_sx,ball_ex,ball_sy,ball_ey;
int x=0,y=0;
unsigned char DIS=0;//向外拓展的距離
unsigned char up_err=0,down_err=0,left_err=0,right_err=0;
while(!up_err || !down_err || !left_err || !right_err)//圖像還沒遍歷完,就不斷尋找
{
DIS+=step;//向外拓展一圈
//在矩形框下邊界尋找
if((start_y-DIS)>=0)//下邊界存在判定
{
for(x=start_x-DIS;x<start_x+DIS;x+=step)
{
if(x<0)x=0;//超右邊界
if(x>camera_W)break;//超左邊界
if(Ball_Match_Color(p[(start_y-DIS)*camera_W+x]))//匹配到的點
{
//從這個點找到匹配大小的色塊
if(Find_Center_Size(p,x,start_y-DIS,&ball_sx,&ball_ex,&ball_sy,&ball_ey))
{
*sx=ball_sx;
*ex=ball_ex;
*sy=ball_sy;
*ey=ball_ey;
return 1;//匹配成功
}
else
{
x+=2;
}
}
}
}
else
{
down_err=1;
}
//矩形框上邊界尋找
if((start_y+DIS)<camera_H)//上邊界存在判定
{
for(x=start_x-DIS;x<start_x+DIS;x+=step)
{
if(x<0)x=0;//超右邊界
if(x>camera_W)break;//超左邊界
if(Ball_Match_Color(p[(start_y+DIS)*camera_W+x]))//匹配到的點
{ //從這個點找到匹配大小的色塊
if(Find_Center_Size(p,x,start_y+DIS,&ball_sx,&ball_ex,&ball_sy,&ball_ey))
{
*sx=ball_sx;
*ex=ball_ex;
*sy=ball_sy;
*ey=ball_ey;
return 1;//匹配成功
}
else
{
x+=2;
}
}
}
}
else
{
up_err=1;
}
//矩形框左邊界尋找
if((start_x+DIS)<camera_W)//左邊界存在判定
{
for(y=start_y-DIS;y<start_y+DIS;y+=step)
{
if(y<0)y=0;//超下邊界處理
if(y>camera_H)break;//超上邊界處理
if(Ball_Match_Color(p[y*camera_W+start_x+DIS]))//匹配到的點
{ //從這個點找到了匹配大小的色塊
if(Find_Center_Size(p,start_x+DIS,y,&ball_sx,&ball_ex,&ball_sy,&ball_ey))
{
*sx=ball_sx;
*ex=ball_ex;
*sy=ball_sy;
*ey=ball_ey;
return 1;//匹配成功
}
else
{
y+=2;
}
}
}
}
else
{
left_err=1;
}
//矩形框右邊界尋找
if((start_x-DIS)>0)//右邊界存在判定
{
for(y=start_y-DIS;y<start_y+DIS;y+=step)
{
if(y<0)y=0;//超下邊界處理
if(y>camera_H)break;//超上邊界處理
if(Ball_Match_Color(p[y*camera_W+start_x-DIS]))//匹配到的點
{ //從這個點找到了匹配大小的色塊
if(Find_Center_Size(p,start_x-DIS,y,&ball_sx,&ball_ex,&ball_sy,&ball_ey))
{
*sx=ball_sx;
*ex=ball_ex;
*sy=ball_sy;
*ey=ball_ey;
return 1;//匹配成功
}
else
{
y+=2;
}
}
}
}
else
{
right_err=1;
}
}
return 0;//匹配失敗
}
/**************************
畫框
******************************/
void Draw_square(unsigned short *p , int start_x , int end_x , int start_y , int end_y , unsigned short color)
{
int x1,x2,y1,y2;
int x,y;
if(start_x==end_x || start_y==end_y)return;//如果不符合就直接返回
/* start_x與end_x哪個大哪個賦值x2,小的賦值x1 */
if(start_x<end_x)
{
x1=start_x;
x2=end_x;
}
else
{
x2=start_x;
x1=end_x;
}
/* start_y與end_y哪個大哪個賦值y2,小的賦值y1 */
if(start_y<end_y)
{
y1=start_y;
y2=end_y;
}
else
{
y2=start_y;
y1=end_y;
}
//超出邊界就取邊界值
if(x1<0)x1=0;
if(x2>(camera_W-1))x2=camera_W-1;
if(y1<0)y1=0;
if(y2>(camera_H-1))y2=camera_H-1;
for(x=x1;x<x2;x++)
{
p[start_y*camera_W+x]=color;
p[end_y*camera_W+x]=color;
}
for(y=y1;y<y2;y++)
{
p[y*camera_W+start_x]=color;
p[y*camera_W+end_x]=color;
}
}
/*查找色塊並畫框標出*/
unsigned char Ball_Task(unsigned short *p)
{
unsigned char sx,ex,sy,ey;
if(Find_Ball(p,Ball_X,Ball_Y,&sx,&ex,&sy,&ey))
{
Ball_X=(sx+ex)/2;
Ball_Y=(sy+ey)/2;
Draw_square(p,sx,ex,sy,ey,0x001F); //0x001F爲RGB565的藍色
return 1;
}
else
{
return 0;
}
}
這些函數都有了之後最後就是主函數對這些函數的調用了
int main(int argc, char **argv)
{
int rv = -1 ;
int result = 0 ;
unsigned char buffer[320*240*3] ;
unsigned short rgb565_buf[320*240] ;
unsigned char rgb888_buf[320*240*3] ;
memset(buffer,0, sizeof(buffer)) ;
rv = read_JPEG_file ("test.jpg",buffer,sizeof(buffer)) ;//讀取OV2640採集到的JPEG圖像解碼爲RGB888數據
if(rv < 0)
{
printf("read_JPEG_file() failed\n") ;
return -1 ;
}
printf("jpeg decompress OK, after decompress size = %d\n",rv) ;
memset(rgb565_buf,0,sizeof(rgb565_buf)) ;
rv = rgb888torgb565(buffer, rv, rgb565_buf, sizeof(rgb565_buf)) ;//RGB888轉RGB565
printf("after rgb888 to rgb565,rgb565 size=%d\n",rv) ;
result = Ball_Task(rgb565_buf) ;//查找符合的色塊
printf("Return value=%d\n",result) ;
memset(rgb888_buf,0,sizeof(rgb888_buf)) ;
rv = rgb565torgb888(rgb565_buf, rv,rgb888_buf, sizeof(rgb888_buf)) ;//RGB888轉RGB565
printf("after rgb565 to rgb888,rgb888 size=%d\n",rv) ;
write_JPEG_file("new_test.jpg",rgb888_buf,160,120,75) ;//RGB888編碼保存jpg圖片文件
printf("rgb write jpeg compress finish!\n") ;
return 0 ;
}/* End Of Main */
最後就是運行出來輸出的圖像,尋找紅色色塊,色塊參數閾值還沒調好,RGB圖像處理時候,容易受到光照變化或陰影的影響,就像最後的效果圖,而且RGB通道並不能很好地反映出物體具體的顏色信息 ,也可能還有RGB轉換、JPEG解碼導致精度丟失造成的原因
如果是顏色比較突兀的就比較好識別