最近自己所在小組做了一個智能家居系統,本人主要負責攝像頭圖像採集部分,需要完成的功能是實現攝像頭數據採集,而且圖片需要在LCD上顯示,需要經過網絡遠程發送,自己小白一個,做之前什麼都不懂,經歷各種查資料請教過後總算出效果了,感觸頗深。這期間CSDN上各位大神的各種博客對自己幫助很大,在此一併謝過!!!!!同時也發現很多博客都只包含一個小部分,感覺如果有一個篇完整的介紹可能對新手會有幫助,因此在此簡單介紹攝像頭採集整個流程。第一次發博客,懇請各位大神多多指教,如有不妥之處,還請見諒。
廢話少說,直接進正題。首先要說明的是我們的攝像頭是在ARM Cortex-A8要下運行的,出來的圖像結果與PC機上會有一定的不同,請各位注意。
1、驅動支持
在那位法國牙醫的無私奉獻下,Linux內核幾乎支持所有的USB攝像頭,不過要想自己的Linux內核支持USB免驅攝像頭,還需要先配置內核,
Device Drivers --->
<*> Multimedia support --->
<*> Video For Linux
[ ] Enable Video For Linux API 1 (DEPRECATED)
[*] Video capture adapters --->
[*] V4L USB devices --->
<*> USB Video Class (UVC)
[*] UVC input events device support
這樣在板子上插入攝像頭後終端就會有顯示:
[root@farsight /]# usb 1-1.1: new full speed USB device using s3c2410-ohci and a ddress 4
uvcvideo: Found UVC 1.00 device Webcam C110 (046d:0829)
input: Webcam C110 as /class/input/input2
同時輸入命令:lsusb 也會有相應信息,在此不就不詳細展開了,網上有很多資料。最主要的是此時進入/dev 目錄下,ls 會新增加一個設備,我的是video0,不同情況下需自己確認,這個設備名很重要。至此,Linux內核對攝像頭的驅動支持就沒問題了。
2、開始操作攝像頭
經典操作v4l2的方法一共也就那麼步,大致爲:打開設備->查看設備功能->設置圖片格式->申請幀緩衝->內存映射->幀緩衝入列->開始採集->讀數據(包括處理數據)->幀緩衝重新入列->關閉設備。看着名字挺霸氣的,其實每一步都是調用內核驅動提供的出來的接口就可以了。
2.1 打開設備
fd = open(dev_name, O_RDWR, 0 );//打開設備文件,阻塞模式
if (fd < 0){
perror("open /dev/video0 fialed! ");
return -1;
}
打開一個open就OK了,注意此處用的是阻塞模式,如果是非阻塞模式(O_NONBLOCK)的話,即使攝像頭尚未捕獲到信息,驅動依舊會把緩存(DQBUFF)裏的東西返回給應用程序,感覺這樣有點不合理,也懂內核爲何要這樣設計。
2.2 查看設備功能
<span style="font-size:14px;">struct v4l2_capability cap;
ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);//查看設備功能
if (ret < 0){
perror("requre VIDIOC_QUERYCAP fialed! \n");
return -1;
}
printf("driver:\t\t%s\n",cap.driver);
printf("card:\t\t%s\n",cap.card);
printf("bus_info:\t%s\n",cap.bus_info);
printf("version:\t%d\n",cap.version);
printf("capabilities:\t%x\n",cap.capabilities);
if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE){
printf("Device %s: supports capture.\n",dev_name);
}
if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING){
printf("Device %s: supports streaming.\n",dev_name);
}</span>
查看設備功能也沒什麼好說的,看代碼就OK啦。
2.3 設置圖片格式
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = WIDTH;
fmt.fmt.pix.height = HEIGHT;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if(-1 == ioctl(fd, VIDIOC_S_FMT, &fmt)){//設置圖片格式
perror("set format failed!");
return -1;
}
if(-1 == ioctl(fd, VIDIOC_G_FMT, &fmt)){//得到圖片格式
perror("set format failed!");
return -1;
}
printf("fmt.type:\t\t%d\n",fmt.type);
printf("pix.pixelformat:\t%c%c%c%c\n", \
fmt.fmt.pix.pixelformat & 0xFF,\
(fmt.fmt.pix.pixelformat >> 8) & 0xFF, \
(fmt.fmt.pix.pixelformat >> 16) & 0xFF,\
(fmt.fmt.pix.pixelformat >> 24) & 0xFF);
printf("pix.width:\t\t%d\n",fmt.fmt.pix.width);
printf("pix.height:\t\t%d\n",fmt.fmt.pix.height);
printf("pix.field:\t\t%d\n",fmt.fmt.pix.field);
也是一個命令就完成:VIDIOC_S_FMT,其中WIDTH,HEGHT 是定義的宏,後面很多地方都要用這兩個參數,定義成宏比傳參方便。V4L2_PIX_FMT_YUYV 指定輸出格式爲YUYV,關於YUYV,RGB等等什麼什麼格式,網上也有很詳細的介紹,比如這篇:談談RGB、YUY2、YUYV、YVYU、UYVY、AYUV。
當然,圖片格式設置也不只這3個,還有像幀率什麼的也是可以設置的。
需要注意的是,對於不用的攝像頭,內核有不一樣的支持,並不是你設置了就一定能用,如果內核中該視頻設備驅動不支持你所設定的圖像格式,視頻驅動會重新修改struct v4l2_format結構體變量的值爲該視頻設備所支持的圖像格式,所以在程序設計中,設定完所有的視頻格式後,要獲取實際的視頻格式,要重新讀取 struct v4l2_format結構體變量。
2.3.1 查看圖片格式
if(-1 == ioctl(fd, VIDIOC_G_FMT, &fmt)){//得到圖片格式
perror("set format failed!");
return -1;
}
printf("fmt.type:\t\t%d\n",fmt.type);
printf("pix.pixelformat:\t%c%c%c%c\n", \
fmt.fmt.pix.pixelformat & 0xFF,\
(fmt.fmt.pix.pixelformat >> 8) & 0xFF, \
(fmt.fmt.pix.pixelformat >> 16) & 0xFF,\
(fmt.fmt.pix.pixelformat >> 24) & 0xFF);
printf("pix.width:\t\t%d\n",fmt.fmt.pix.width);
printf("pix.height:\t\t%d\n",fmt.fmt.pix.height);
printf("pix.field:\t\t%d\n",fmt.fmt.pix.field); <span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;"> </span>
2.4 申請幀緩衝
<span style="font-size:14px;">req.count = 5;//申請緩衝數量
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
ioctl(fd, VIDIOC_REQBUFS, &req);//申請緩衝,
if (req.count < 2){
perror("buffer memory is Insufficient! \n");
return -1;
}</span>
2.5 映射用戶空間
<span style="font-size:14px;"> yuyv_buffers0 = calloc(req.count, sizeof(*yuyv_buffers0));//內存中建立對應空間
for (n_buffers = 0; n_buffers < req.count; ++n_buffers){
struct v4l2_buffer buf;//驅動中的一幀
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;
if (-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf)){//映射用戶空間
perror("VIDIOC_QUERYBUF error!\n");
return -1;
}
yuyv_buffers0[n_buffers].length = buf.length;
yuyv_buffers0[n_buffers].start =(char*) mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
if (MAP_FAILED == yuyv_buffers0[n_buffers].start){
close(fd);
perror("mmap faild! \n");
return -1;
}
printf("Frame buffer %d: address = 0x%x, length = %d \n",req.count, (unsigned int)yuyv_buffers0[n_buffers].start, yuyv_buffers0[n_buffers].length);
}</span>
好像也沒什麼好說的..........不過這篇博客說得挺詳細的,直接引用了,博主勿怪..... 和菜鳥一起學linux之V4L2攝像頭應用流程2.6 申請到的緩衝進入隊列
<span style="font-size:14px;"> </span><span style="font-size:14px;"> for (i=0; i<n_buffers; ++i){
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if ( -1 == ioctl(fd, VIDIOC_QBUF, &buf)){//申請到的緩衝進入隊列
close(fd);
perror("VIDIOC_QBUF failed! \n");
return -1;
}
}</span>
直接看代碼......2.7 開始捕捉圖像數據
<span style="font-size:14px;"> type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == ioctl(fd, VIDIOC_STREAMON, &type)){//開始捕捉圖像數據
close(fd);
perror("VIDIOC_STREAMON failed! ");
exit(-1);
} </span>
函數執行成功後,攝像頭開始採數據,一般來說可以用一個select判斷一幀視頻數據是否採集完成,當視頻設備驅動完成一幀視頻數據採集並保存到視頻緩衝區中時,select函數返回,應用程序接着可以讀取視頻數據;否則select函數阻塞直到視頻數據採集完成。<span style="font-size:14px;"> </span><span style="font-size:14px;"> enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fd_set fds;
struct timeval tv;
int r;
FD_ZERO(&fds);//將指定文件描述符集清空
FD_SET(fd, &fds);//在文件描述符集合中增加一個新的文件描述符
tv.tv_sec = 2;//time out
tv.tv_usec = 0;
r = select(fd+1, &fds, NULL, NULL, &tv);//判斷攝像頭是否準備好,tv是定時
if(-1 == r){
if(EINTR == errno){
printf("select erro! \n");
}
}
else if(0 == r){
printf("select timeout! \n");//超時
return 1;
//exit(EXIT_FAILURE);
}
read_frame(); //處理一幀數據</span>
2.8 讀數據
<span style="font-size:14px;"> file_fd = fopen(path1, "w");//yuyv圖片
if (file_fd < 0){
perror("open test_mmap.jpg fialed! \n");
exit(-1);
}
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd, VIDIOC_DQBUF, &buf);//出列採集的幀緩衝,成功返回0
if(0 != ret){
printf("VIDIOC_DQBUF failed!\n");
exit(-1);<pre name="code" class="objc"> }<pre name="code" class="objc"> ret = fwrite(yuyv_buffers0[buf.index].start, yuyv_buffers0[buf.index].length, 1, file_fd);//將攝像頭採集得到的yuyv數據寫入文件中<pre name="code" class="objc"> if(ret <= 0){
printf("write yuyv failed!\n");
exit(-1);
}</span><span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;"> </span><span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;"> </span>
這裏有個幀緩衝出列的概念,也就是讀取裏面的數據並保存置文件中,不過保存的文件是yuyv圖片,直接打開自然是不行的,需要用工具YUVViewer.exe,並且工具軟件裏面的參數配置也必須符合你設定的圖片格式,這樣才能看到真正的效果。到此爲止,恭喜你,已經成功的邁出第一步了。是的,你沒聽錯,才第一步,一張圖片要直接在LCD上顯示,要遠程發送,是不能直接用yuyv數據的,還得經過一系列轉換。2.9 幀緩衝入列
<span style="font-size:14px;"> ret = ioctl(fd, VIDIOC_QBUF,&buf);//幀緩衝入列
if(0 != ret){
printf("VIDIOC_QBUF failed!\n");
exit(-1);
}</span>
讀取完幀緩衝裏面的數據,別忘了將其入列,方便下次使用。2.10 關閉設備
<span style="font-size:14px;">static void v4l2_close(void)
{
int i=0;
unmap:
for(i=0; i<n_buffers; ++i){
if(-1 == munmap(yuyv_buffers0[i].start, yuyv_buffers0[i].length)){
printf("munmap error! \n");
exit(-1);
}
}
close(fd);
exit(EXIT_SUCCESS);
}</span>
關閉設備一個close就可以了,不過需要注意的是還有一個解除內存映射的工作需要完成。3.1 yuyv 轉RGB
<span style="font-size:14px;">void yuyv_to_rgb(unsigned char* yuv,unsigned char* rgb)
{
unsigned int i;
unsigned char* y0 = yuv + 0;
unsigned char* u0 = yuv + 1;
unsigned char* y1 = yuv + 2;
unsigned char* v0 = yuv + 3;
unsigned char* r0 = rgb + 0;
unsigned char* g0 = rgb + 1;
unsigned char* b0 = rgb + 2;
unsigned char* r1 = rgb + 3;
unsigned char* g1 = rgb + 4;
unsigned char* b1 = rgb + 5;
float rt0 = 0, gt0 = 0, bt0 = 0, rt1 = 0, gt1 = 0, bt1 = 0;
for(i = 0; i <= (WIDTH * HEIGHT) / 2 ;i++)
{
bt0 = 1.164 * (*y0 - 16) + 2.018 * (*u0 - 128);
gt0 = 1.164 * (*y0 - 16) - 0.813 * (*v0 - 128) - 0.394 * (*u0 - 128);
rt0 = 1.164 * (*y0 - 16) + 1.596 * (*v0 - 128);
bt1 = 1.164 * (*y1 - 16) + 2.018 * (*u0 - 128);
gt1 = 1.164 * (*y1 - 16) - 0.813 * (*v0 - 128) - 0.394 * (*u0 - 128);
rt1 = 1.164 * (*y1 - 16) + 1.596 * (*v0 - 128);
if(rt0 > 250) rt0 = 255;
if(rt0< 0) rt0 = 0;
if(gt0 > 250) gt0 = 255;
if(gt0 < 0) gt0 = 0;
if(bt0 > 250) bt0 = 255;
if(bt0 < 0) bt0 = 0;
if(rt1 > 250) rt1 = 255;
if(rt1 < 0) rt1 = 0;
if(gt1 > 250) gt1 = 255;
if(gt1 < 0) gt1 = 0;
if(bt1 > 250) bt1 = 255;
if(bt1 < 0) bt1 = 0;
*r0 = (unsigned char)rt0;
*g0 = (unsigned char)gt0;
*b0 = (unsigned char)bt0;
*r1 = (unsigned char)rt1;
*g1 = (unsigned char)gt1;
*b1 = (unsigned char)bt1;
yuv = yuv + 4;
rgb = rgb + 6;
if(yuv == NULL)
break;
y0 = yuv;
u0 = yuv + 1;
y1 = yuv + 2;
v0 = yuv + 3;
r0 = rgb + 0;
g0 = rgb + 1;
b0 = rgb + 2;
r1 = rgb + 3;
g1 = rgb + 4;
b1 = rgb + 5;
}
}</span>
出列幀緩衝後就可以調用該函數,rgb 需要先開闢大小爲WIDTH * HEIGTH * 3的空間,因爲我們用的RGB是24位格式,3個字節分別代表一個像素點的R、G、B,根據公式轉換就好了。3.2 RGB 轉BMP
<span style="font-size:14px;">void rgb_to_bmp(unsigned char* pdata, FILE* bmp_fd)
{
//分別爲rgb數據,要保存的bmp文件名
int size = WIDTH * HEIGHT * 3 * sizeof(char); // 每個像素點3個字節
// 位圖第一部分,文件信息
BMPFILEHEADER_T bfh;
bfh.bfType = (unsigned short)0x4d42; //bm
bfh.bfSize = size // data size
+ sizeof( BMPFILEHEADER_T ) // first section size
+ sizeof( BMPINFOHEADER_T ) // second section size
;
bfh.bfReserved1 = 0; // reserved
bfh.bfReserved2 = 0; // reserved
bfh.bfOffBits = sizeof( BMPFILEHEADER_T )+ sizeof( BMPINFOHEADER_T );//真正的數據的位置
// printf("bmp_head== %ld\n", bfh.bfOffBits);
// 位圖第二部分,數據信息
BMPINFOHEADER_T bih;
bih.biSize = sizeof(BMPINFOHEADER_T);
bih.biWidth = WIDTH;
bih.biHeight = -HEIGHT;//BMP圖片從最後一個點開始掃描,顯示時圖片是倒着的,所以用-height,這樣圖片就正了
bih.biPlanes = 1;//爲1,不用改
bih.biBitCount = 24;
bih.biCompression = 0;//不壓縮
bih.biSizeImage = size;
bih.biXPelsPerMeter = 0;//像素每米
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;//已用過的顏色,爲0,與bitcount相同
bih.biClrImportant = 0;//每個像素都重要
fwrite( &bfh, 8, 1, bmp_fd);
fwrite(&bfh.bfReserved2, sizeof(bfh.bfReserved2), 1, bmp_fd);
fwrite(&bfh.bfOffBits, sizeof(bfh.bfOffBits), 1, bmp_fd);
fwrite(&bih, sizeof(BMPINFOHEADER_T), 1, bmp_fd);
fwrite(pdata, size, 1, bmp_fd);
} </span>
說是轉,其實就是一個另存爲,只是加個格式頭而已,需要注意的也只有size, biWidth, biHeight
這幾個參數而已,如果你發現你生成的BMP圖是倒立的,改下bih.biHeight = -HEIGHT就OK了。3.3 RGB放縮算法
void rgb_stretch(char* src_buf, char* dest_buf, int des_width, int des_hight)
{
//最臨近插值算法
//雙線性內插值算法放大後馬賽克很嚴重 而且幀率下降嚴重
printf("des_width = %d, des_hight = %d \n ",des_width, des_hight);
double rate_w = (double) WIDTH / des_width;//橫向放大比
double rate_h = (double) HEIGHT / des_hight;//軸向放大比
int dest_line_size = ((des_width * BITCOUNT +31) / 32) * 4;
int src_line_size = BITCOUNT * WIDTH / 8;
int i = 0, j = 0, k = 0;
for (i = 0; i < des_hight; i++)//desH 目標高度
{
//選取最鄰近的點
int t_src_h = (int)(rate_h * i + 0.5);//rateH (double)srcH / desH;
for (j = 0; j < des_width; j++)//desW 目標寬度
{
int t_src_w = (int)(rate_w * j + 0.5);
memcpy(&dest_buf[i * dest_line_size] + j * BITCOUNT / 8, \
&src_buf[t_src_h * src_line_size] + t_src_w * BITCOUNT / 8,\
BITCOUNT / 8);
}
}
}
也嘗試了雙線性內插值算法,從原理上分析,應該是雙線性內插值算法效果更好,結果卻不是這樣,爲什麼是這樣,我這種菜鳥也弄不明白......這裏補充一個第2.8步的詳細調用:
int numb = 0;
static int read_frame(char *rgb_buffers)
{
struct v4l2_buffer buf;
int ret =0;
static char path1[30];
static char path2[30];
FILE *file_fd;//yuyv 圖片文件流
FILE *bmp_fd;//bmp 圖片文件流
numb ++;
sprintf(path1, "./test_mmap%d.jpg", numb);//文件名
sprintf(path2, "./image%d.bmp", numb);
printf("path1=%s, path2=%s %d\n", path1, path2, numb);
file_fd = fopen(path1, "w");//yuyv圖片
if (file_fd < 0){
perror("open test_mmap.jpg fialed! \n");
exit(-1);
}
bmp_fd = fopen(path2, "w");//bmp圖片
if (bmp_fd < 0){
perror("open image.bmp failed!");
exit(-1);
}
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd, VIDIOC_DQBUF, &buf);//出列採集的幀緩衝,成功返回0
if(0 != ret){
printf("VIDIOC_DQBUF failed!\n");
exit(-1);
}
yuyv_to_rgb(yuyv_buffers0[buf.index].start, rgb_buffers);//yuyv -> rgb24
rgb_stretch(rgb_buffers, dest_buffers, DEST_WIDTH, DEST_HEIGHT);//176 X 144 -> 320 X 240
rgb24_to_rgb565(dest_buffers, rgb565_buffers);//rgb24 -> rgb565
ret = fwrite(yuyv_buffers0[buf.index].start, yuyv_buffers0[buf.index].length, 1, file_fd);//將攝像頭採集得到的yuyv數據寫入文件中
if(ret <= 0){
printf("write yuyv failed!\n");
exit(-1);
}
rgb_to_bmp(rgb565_buffers, bmp_fd);//rgb -> bmp
ret = ioctl(fd, VIDIOC_QBUF,&buf);//幀緩衝入列
if(0 != ret){
printf("VIDIOC_QBUF failed!\n");
exit(-1);
}
fclose(file_fd);
fclose(bmp_fd);
return 1;
}
這個函數,調用了以上3.1 - 3.3 列舉出來的所有函數,當然了,是先放大還是先保存怎樣怎樣的,大家都可以自己調整。3.4 RGB 轉JPEG
衆說周知,jpeglib 是一個強大的jpeg類庫,直接調用裏面的一些接口函數,就能直接實現一些異常複雜的jpeg處理。但老版jpeglib 最不好的地方就是隻支持文件流的輸入輸出,不支持從內存中解壓或者壓縮至內存中,實際工程中哪會處處都是文件操作???因此用起來挺麻煩的,也照着網上的資料,修改了jpeglib的源碼,結果渣渣技術,一運行就報段錯誤,搞了2天,放棄了,請教老師,結果,一句代碼就解決了我2天的麻煩。哪句代碼?
首先是編譯源碼和準備開發環境。
1.感覺編譯jpeglib ,準備環境也沒有網上那些資料說得那麼簡單樣,因此在此還是簡單介紹下jpeglib的編譯以及使用環境的準備。
下載源碼,注意最好是用新版的jpeglib-8b 版,因爲老版本貌似是不支持內存中操作的(當然也沒下載到老版源碼,沒實驗,有興趣的同學可以試試),這裏有個鏈接,自己的資源:jpegsrc.v8b.tar.gz。
2.解壓。
./configure CC=/opt/arm-cortex_a8/bin/arm-cortex_a8-linux-gnueabi-gcc LD=/opt/arm-cortex_a8/bin/arm-cortex_a8-linux-gnueabi-ld --host=arm-linux --prefix=/opt/arm-cortex_a8/arm-cortex_a8-linux-gnueabi --exec-prefix=/opt/arm-cortex_a8/arm-cortex_a8-linux-gnueabi --enable-shared --enable-static
配置好後,make,沒報錯,就make install , 千萬不能忘記make install !!!
make install 成功後會在我的/opt/arm-cortex_a8/arm-cortex_a8-linux-gnueabi/lib下生成5個庫:libjpeg.a lalibjpeg.la libjpeg.so libjpeg.so.8 libjpeg.so.8.0.2 ,會在我的/opt/arm-cortex_a8/arm-cortex_a8-linux-gnueabi/include下生成4個頭文件:jconfig.h jerror.h jmorecfg.h jpeglib.h, 這幾個庫和頭文件是後面會用到。
注:(1)針對ARM下使用的jpeglib,CC:交叉編譯工具鏈,CC 後面接的路徑就是我的交叉編譯器的路徑,你的在哪裏自己才清楚啊; LD:鏈接用,同上; --host:指定主機,得是arm-linux,網上有資料說是arm-unkown-linux, 實測不行;--prefix:生成的頭文件存放目錄; --exec-prefix:生成的動態庫靜態庫存放目錄,這個很關鍵,必須得是arm-cortex_a8/arm-cortex_a8-linux-gnueabi;--enable-shared : 用GNU libtool編譯成動態鏈接庫 。想強調的一點是以上幾個參數,大家最好都配置上,注意嚴格檢查路徑,"="前後不要留空格,網上有些資料只說了要配置CC,LD,沒說配置host,結果自然還是使用不了。
(2)如果ARM板子是掛載的根文件系統,那還必須把那5個庫拷貝到rootfs/lib 目錄下。
(3)如果你是要在PC機上使用jpeglib 庫的話,那麼只需把生成庫的路徑改爲/lib 或者 /usr/lib 就行了。
4.所謂的準備開發環境,一是上面剛剛說的在正確的位置下準備好那5個庫,二是還需要把那4個頭文件放到你的工程目錄下,這樣基本上就沒問題了。
心裏沒底的同學可以先測試下jpeglib庫能否使用,只需在一個最簡單的.C中,包含<jpeglib.h>,編譯時加上-ljpeg ,編譯(這個編譯器一定得是你上面配置的CC後面跟的那編譯器哦!)不報錯,那就是真正的沒問題了。反之,報XXXXlib 找不到,XXXX.h 找不到,那就得好好檢查上面幾步了。
庫準備好了就可以直接用了:
long rgb_to_jpeg(const char *rgb, char *jpeg)
{
long jpeg_size;
struct jpeg_compress_struct jcs;
struct jpeg_error_mgr jem;
JSAMPROW row_pointer[1];
int row_stride;
jcs.err = jpeg_std_error(&jem);
jpeg_create_compress(&jcs);
jpeg_mem_dest(&jcs, jpeg, &jpeg_size);//就是這個函數!!!!!!!
jcs.image_width = WIDTH;
jcs.image_height = HEIGHT;
jcs.input_components = 3;//1;
jcs.in_color_space = JCS_RGB;//JCS_GRAYSCALE;
jpeg_set_defaults(&jcs);
jpeg_set_quality(&jcs, 180, TRUE);
jpeg_start_compress(&jcs, TRUE);
row_stride =jcs.image_width * 3;
while(jcs.next_scanline < jcs.image_height){//對每一行進行壓縮
row_pointer[0] = &rgb[jcs.next_scanline * row_stride];
(void)jpeg_write_scanlines(&jcs, row_pointer, 1);
}
jpeg_finish_compress(&jcs);
jpeg_destroy_compress(&jcs);
#ifdef JPEG //jpeg 保存,測試用
FILE *jpeg_fd;
sprintf(path3, "./jpeg%d.jpg", numb);
jpeg_fd = fopen(path3,"w");
if(jpeg_fd < 0 ){
perror("open jpeg.jpg failed!\n");
exit(-1);
}
fwrite(jpeg, jpeg_size, 1, jpeg_fd);
close(jpeg_fd);
#endif
return jpeg_size;
}
上面的代碼註釋不是很詳細,jpeglib 詳細使用方法看這篇博客就OK:利用jpeglib壓縮圖像爲jpg格式,想必大家也發現了,是的,如果你用的是jpeg_stdio_dest()函數,那麼就是文件操作,最後壓縮完成的結果直接保存在文件中,反之,如果你用的是jpeg_mem_dest()函數,那麼壓縮完成的結果就保存在內存中。
這裏有一個以上功能的完整版程序,完整版與上面的代碼片段有稍許不同,請注意。Linux 下V4l2攝像頭採集圖片,實現yuyv轉RGB,RGB轉BMP,RGB伸縮,RGB轉JPEG(保存到內存中),JPEG經UDP發送功能,或者您也可以直接到我的資源中去下載。