lvgl显示原始像素图像数据,常用canvas
此处为了显示出一个圆,我进行多方测试,发现canvas只能将图像数据原样显示,为了显示出圆,就需要在原始数据上做文章,可以用算法,直接做出来一个圆,其他区域,透明度设置为0x00 实际显示时,发现锯齿很严重,解决这个问题,可以在canvas上再放一个obj,设置border进行遮盖
核心算法如下
int MJPEGToBGRA(void *mjpeg, int bytesused, unsigned char* bgra_data) { struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; JSAMPARRAY buffer; int row_stride; // 设置错误处理 cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; if (setjmp(jerr.setjmp_buffer)) { jpeg_destroy_decompress(&cinfo); return -1; } // 初始化解压缩对象 jpeg_create_decompress(&cinfo); // 指定输入数据 jpeg_mem_src(&cinfo, mjpeg, bytesused); // 读取 JPEG 文件头 jpeg_read_header(&cinfo, TRUE); // 开始解压缩 jpeg_start_decompress(&cinfo); row_stride = cinfo.output_width * cinfo.output_components; int width=cinfo.output_width; int heigh=cinfo.output_height; // mydebugNum(width) // mydebugNum(heigh) // 圆形,y索引0->240 ,对应 /* # python代码,生成数组 import math def increment_if_half_or_more(number): # return number fractional_part = number- int(number) if fractional_part > 0.7: return int(number) + 1 else: return int(number) radius=120 for y in range(radius*2+1): x1 = +math.sqrt(radius*radius-(y-radius)*(y-radius))+radius x1=increment_if_half_or_more(x1) x2 = -math.sqrt(radius*radius-(y-radius)*(y-radius))+radius x2=increment_if_half_or_more(x2) print("{",x2,",",x1,"},") */ static circle_240[][2] = { { 0 , 0 }, { 104 , 135 }, { 98 , 142 }, { 93 , 146 }, { 89 , 151 }, { 86 , 154 }, { 82 , 157 }, { 79 , 160 }, { 77 , 163 }, { 74 , 165 }, { 72 , 168 }, { 70 , 170 }, { 67 , 172 }, { 65 , 174 }, { 64 , 176 }, { 62 , 178 }, { 60 , 180 }, { 58 , 181 }, { 57 , 183 }, { 55 , 185 }, { 53 , 186 }, { 52 , 188 }, { 51 , 189 }, { 49 , 190 }, { 48 , 192 }, { 46 , 193 }, { 45 , 194 }, { 44 , 196 }, { 43 , 197 }, { 42 , 198 }, { 40 , 199 }, { 39 , 200 }, { 38 , 201 }, { 37 , 202 }, { 36 , 203 }, { 35 , 205 }, { 34 , 205 }, { 33 , 206 }, { 32 , 207 }, { 31 , 208 }, { 30 , 209 }, { 29 , 210 }, { 29 , 211 }, { 28 , 212 }, { 27 , 213 }, { 26 , 213 }, { 25 , 214 }, { 25 , 215 }, { 24 , 216 }, { 23 , 217 }, { 22 , 217 }, { 22 , 218 }, { 21 , 219 }, { 20 , 219 }, { 20 , 220 }, { 19 , 221 }, { 18 , 221 }, { 18 , 222 }, { 17 , 223 }, { 16 , 223 }, { 16 , 224 }, { 15 , 224 }, { 15 , 225 }, { 14 , 225 }, { 14 , 226 }, { 13 , 226 }, { 13 , 227 }, { 12 , 227 }, { 12 , 228 }, { 11 , 228 }, { 11 , 229 }, { 10 , 229 }, { 10 , 230 }, { 9 , 230 }, { 9 , 231 }, { 9 , 231 }, { 8 , 231 }, { 8 , 232 }, { 7 , 232 }, { 7 , 233 }, { 7 , 233 }, { 6 , 233 }, { 6 , 234 }, { 6 , 234 }, { 5 , 234 }, { 5 , 235 }, { 5 , 235 }, { 4 , 235 }, { 4 , 235 }, { 4 , 236 }, { 4 , 236 }, { 3 , 236 }, { 3 , 236 }, { 3 , 237 }, { 3 , 237 }, { 2 , 237 }, { 2 , 237 }, { 2 , 238 }, { 2 , 238 }, { 2 , 238 }, { 1 , 238 }, { 1 , 238 }, { 1 , 238 }, { 1 , 239 }, { 1 , 239 }, { 1 , 239 }, { 1 , 239 }, { 1 , 239 }, { 0 , 239 }, { 0 , 239 }, { 0 , 239 }, { 0 , 239 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 240 }, { 0 , 239 }, { 0 , 239 }, { 0 , 239 }, { 0 , 239 }, { 1 , 239 }, { 1 , 239 }, { 1 , 239 }, { 1 , 239 }, { 1 , 239 }, { 1 , 238 }, { 1 , 238 }, { 1 , 238 }, { 2 , 238 }, { 2 , 238 }, { 2 , 238 }, { 2 , 237 }, { 2 , 237 }, { 3 , 237 }, { 3 , 237 }, { 3 , 236 }, { 3 , 236 }, { 4 , 236 }, { 4 , 236 }, { 4 , 235 }, { 4 , 235 }, { 5 , 235 }, { 5 , 235 }, { 5 , 234 }, { 6 , 234 }, { 6 , 234 }, { 6 , 233 }, { 7 , 233 }, { 7 , 233 }, { 7 , 232 }, { 8 , 232 }, { 8 , 231 }, { 9 , 231 }, { 9 , 231 }, { 9 , 230 }, { 10 , 230 }, { 10 , 229 }, { 11 , 229 }, { 11 , 228 }, { 12 , 228 }, { 12 , 227 }, { 13 , 227 }, { 13 , 226 }, { 14 , 226 }, { 14 , 225 }, { 15 , 225 }, { 15 , 224 }, { 16 , 224 }, { 16 , 223 }, { 17 , 223 }, { 18 , 222 }, { 18 , 221 }, { 19 , 221 }, { 20 , 220 }, { 20 , 219 }, { 21 , 219 }, { 22 , 218 }, { 22 , 217 }, { 23 , 217 }, { 24 , 216 }, { 25 , 215 }, { 25 , 214 }, { 26 , 213 }, { 27 , 213 }, { 28 , 212 }, { 29 , 211 }, { 29 , 210 }, { 30 , 209 }, { 31 , 208 }, { 32 , 207 }, { 33 , 206 }, { 34 , 205 }, { 35 , 205 }, { 36 , 203 }, { 37 , 202 }, { 38 , 201 }, { 39 , 200 }, { 40 , 199 }, { 42 , 198 }, { 43 , 197 }, { 44 , 196 }, { 45 , 194 }, { 46 , 193 }, { 48 , 192 }, { 49 , 190 }, { 51 , 189 }, { 52 , 188 }, { 53 , 186 }, { 55 , 185 }, { 57 , 183 }, { 58 , 181 }, { 60 , 180 }, { 62 , 178 }, { 64 , 176 }, { 65 , 174 }, { 67 , 172 }, { 70 , 170 }, { 72 , 168 }, { 74 , 165 }, { 77 , 163 }, { 79 , 160 }, { 82 , 157 }, { 86 , 154 }, { 89 , 151 }, { 93 , 146 }, { 98 , 142 }, { 104 , 135 }, { 120 , 120 }, }; // 处理每一行数据 buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1); // row 0->480 for (int row = 0; row < cinfo.output_height; row++) { jpeg_read_scanlines(&cinfo, buffer, 1); if(row%2>0){continue;} // row 0->640 for (int col = 160; col < cinfo.output_width; col++) { uint8_t r = buffer[0][col * cinfo.output_components + 0]; uint8_t g = buffer[0][col * cinfo.output_components + 1]; uint8_t b = buffer[0][col * cinfo.output_components + 2]; uint8_t a = 0x00; // Alpha channel, set to 255 (opaque) // 画圆 int y=row/2; int x=(col-160)/2; if(circle_240[y][0]<=x&&x<=circle_240[y][1]){ a = 255; }//有效 // 顺时针旋转90度 int t=x; x=240-y; y=t; // 左右对调 x=240-x; int offset = (y * 240 + x) * 4; bgra_data[offset + 0] = b; bgra_data[offset + 1] = g; bgra_data[offset + 2] = r; bgra_data[offset + 3] = a; } } // 完成解压缩 jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); return 0; } static char dev_video[16]; static void *handle_camera(void *arg){//获取摄像头数据 pthread_t pthid_handle_p=param_face.pthid_handle; #define VIDEO_BUFFER_COUNT 3 // 缓冲区大小 unsigned char* video_buff_buff[VIDEO_BUFFER_COUNT]; memset(video_buff_buff,0x00,sizeof(video_buff_buff)); int video_buff_buff_len=0; // 摄像头基础参数配置 // int video_camera_width=CAM_FACE_WIDTH; // int video_camera_height=CAM_FACE_HEIGHT; int video_camera_width=640; int video_camera_height=480; int video_camera_format=V4L2_PIX_FMT_MJPEG; // 开启摄像头 int video_fd = open(dev_video, O_RDWR /* required */ | O_NONBLOCK, 0); if (-1 == video_fd) { fprintf(stderr, "Cannot open '%s': %d, %s\n", dev_video, errno, strerror(errno)); goto err; } // struct v4l2_capability cap; ioctl(video_fd, VIDIOC_QUERYCAP, &cap); struct v4l2_fmtdesc dis_fmtdesc; dis_fmtdesc.index = 0; dis_fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; printf("-----------------------支持格式---------------------\n"); while (ioctl(video_fd, VIDIOC_ENUM_FMT, &dis_fmtdesc) != -1) { printf("\t%d.%s\n", dis_fmtdesc.index + 1,dis_fmtdesc.description); dis_fmtdesc.index++; } struct v4l2_format video_format; video_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; video_format.fmt.pix.width = video_camera_width; //摄像头宽度 video_format.fmt.pix.height = video_camera_height; // 摄像头高度 video_format.fmt.pix.pixelformat = video_camera_format; //数据格式 if (ioctl(video_fd, VIDIOC_S_FMT, &video_format) < 0) { printf(" setting the data format failed!\n"); mydebug; goto err_video_fd; } printf(" fmt.type = %d\n", video_format.type); printf(" fmt.fmt.pix.width = %d\n", video_format.fmt.pix.width); printf(" fmt.fmt.pix.height = %d\n", video_format.fmt.pix.height); printf(" fmt.fmt.pix.pixelformat = %s\n", get_format_name(video_format.fmt.pix.pixelformat)); printf(" fmt.fmt.pix.field = %d\n", video_format.fmt.pix.field); /*3. 申请缓冲区*/ struct v4l2_requestbuffers video_requestbuffers; memset(&video_requestbuffers, 0, sizeof(struct v4l2_requestbuffers)); video_requestbuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; video_requestbuffers.count = VIDEO_BUFFER_COUNT; video_requestbuffers.memory = V4L2_MEMORY_MMAP; if (ioctl(video_fd, VIDIOC_REQBUFS, &video_requestbuffers)){ printf(" setting VIDIOC_REQBUFS failed!\n"); mydebug; goto err_video_fd; } printf("成功申请的缓冲区数量:%d\n", video_requestbuffers.count); /*4. 得到每个缓冲区的地址: 将申请的缓冲区映射到进程空间*/ struct v4l2_buffer video_buffer; memset(&video_buffer, 0, sizeof(struct v4l2_buffer)); int i; for (i = 0; i < video_requestbuffers.count; i++) { video_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; video_buffer.index = i; video_buffer.memory = V4L2_MEMORY_MMAP; if (ioctl(video_fd, VIDIOC_QUERYBUF, &video_buffer)){ goto err_streanon; } /*映射缓冲区的地址到进程空间*/ video_buff_buff[i] = mmap(NULL, video_buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, video_fd, video_buffer.m.offset); video_buff_buff_len = video_buffer.length; printf("第%d个缓冲区地址:%#X\n", i, video_buff_buff[i]); } /*5. 将缓冲区放入到采集队列*/ memset(&video_buffer, 0, sizeof(struct v4l2_buffer)); for (i = 0; i < video_requestbuffers.count; i++) { video_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; video_buffer.index = i; video_buffer.memory = V4L2_MEMORY_MMAP; if (ioctl(video_fd, VIDIOC_QBUF, &video_buffer)) { printf("VIDIOC_QBUF error\n"); goto err_streanon; } } printf("启动摄像头采集\n"); /*6. 启动摄像头采集*/ int v4l2_buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(video_fd, VIDIOC_STREAMON, &v4l2_buf_type)) { printf("VIDIOC_STREAMON error\n"); goto err_strean_off; } // struct v4l2_buffer video_buffer;//采集数据用 memset(&video_buffer, 0, sizeof(struct v4l2_buffer)); /*3. 读取摄像头的数据*/ struct pollfd video_fds; video_fds.events = POLLIN; video_fds.fd = video_fd; int flag_loss = 0; int bytesused = 0; void* param=NULL; while (pthid_handle_p==param_face.pthid_handle) { poll(&video_fds, 1, -1); /*得到缓冲区的编号*/ video_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; video_buffer.memory = V4L2_MEMORY_MMAP; ioctl(video_fd, VIDIOC_DQBUF, &video_buffer); param=video_buff_buff[video_buffer.index]; bytesused=video_buffer.bytesused; if(flag_loss%2==0){ pthread_mutex_lock(¶m_face.fastmutexcp); if(param_face.flag_check_camera_data==2){ pthread_mutex_unlock(¶m_face.fastmutexcp); if (-1 == ioctl(video_fd, VIDIOC_QBUF, &video_buffer)){ printf("VIDIOC_QBUF buf.index %d failed\n", video_buffer.index); goto err_strean_off; } continue; } param_face.flag_check_camera_data=2; pthread_mutex_unlock(¶m_face.fastmutexcp); // 数据解析 if(video_camera_format==V4L2_PIX_FMT_YUYV){ yuv_to_rgb(param, yuv_buffer_bgra,NULL, video_camera_width, video_camera_height); } else if(video_camera_format==V4L2_PIX_FMT_MJPEG){ MJPEGToBGRA(param, bytesused, yuv_buffer_bgra); } pthread_mutex_lock(¶m_face.fastmutexcp); param_face.flag_check_camera_data=1; pthread_mutex_unlock(¶m_face.fastmutexcp); } flag_loss+=1; //printf("buffer size: %d\r\n", (vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8)); /*将缓冲区放入采集队列*/ if (-1 == ioctl(video_fd, VIDIOC_QBUF, &video_buffer)){ printf("VIDIOC_QBUF buf.index %d failed\n", video_buffer.index); goto err_strean_off; } continue; } mydebug; err_strean_off: ioctl(video_fd, VIDIOC_STREAMOFF, &v4l2_buf_type); err_streanon: mydebugNum(video_buff_buff_len); for (int i = 0; i < VIDEO_BUFFER_COUNT; i++) { mydebug;munmap(video_buff_buff[i], video_buff_buff_len);mydebug; } mydebug; err_video_fd: mydebug; close(video_fd); mydebug; err: return NULL; }
lvgl相关核心代码
// canvas lv_obj_t* appObj_canvas_p=lv_obj_create(appObjUiParent); lv_obj_set_size(appObj_canvas_p,CAMERA_SHOW_WIDTH+50, CAMERA_SHOW_HEIGH+50); lv_obj_set_style_bg_opa(appObj_canvas_p,LV_OPA_0,LV_STATE_DEFAULT); lv_obj_set_style_border_width(appObj_canvas_p,0,LV_STATE_DEFAULT); lv_obj_set_style_radius(appObj_canvas_p,0,LV_STATE_DEFAULT); lv_obj_align(appObj_canvas_p, LV_ALIGN_CENTER, 0, -80); // CANVAS appObjCamera = lv_canvas_create(appObj_canvas_p);//lv_scr_act() lv_obj_align(appObjCamera, LV_ALIGN_CENTER, 0, 0); lv_obj_set_size(appObjCamera,CAMERA_SHOW_WIDTH, CAMERA_SHOW_HEIGH); lv_obj_set_style_radius(appObjCamera,CAMERA_SHOW_WIDTH/2,LV_STATE_DEFAULT); lv_obj_set_style_border_color(appObjCamera,lv_color_hex(0xffffffff),LV_STATE_DEFAULT); lv_obj_set_style_border_width(appObjCamera,0,LV_STATE_DEFAULT); // memset(bufCameraBigCanvas,0x00,CAMERA_SHOW_WIDTH*CAMERA_SHOW_HEIGH*4);mydebug; lv_canvas_set_buffer(appObjCamera, bufCameraBigCanvas, CAMERA_SHOW_WIDTH, CAMERA_SHOW_HEIGH, LV_IMG_CF_TRUE_COLOR_ALPHA);mydebug; // lv_canvas_copy_buf(appObjCamera,bufCameraBigCanvas,0,0,CAMERA_SHOW_WIDTH, CAMERA_SHOW_HEIGH);mydebug; lv_obj_t* appObj_p2=lv_obj_create(appObj_canvas_p); lv_obj_set_size(appObj_p2,CAMERA_SHOW_WIDTH+4, CAMERA_SHOW_HEIGH+4); lv_obj_set_style_border_width(appObj_p2,4,LV_STATE_DEFAULT); lv_obj_set_style_radius(appObj_p2,CAMERA_SHOW_WIDTH/2+2,LV_STATE_DEFAULT); lv_obj_set_style_border_color(appObj_p2,lv_color_hex(0xffffffff),LV_STATE_DEFAULT); lv_obj_set_style_bg_opa(appObj_p2,LV_OPA_0,LV_STATE_DEFAULT); lv_obj_align(appObj_p2, LV_ALIGN_CENTER, 0, 0); if(1){ lv_obj_t* appObj_canvas_p=lv_obj_create(appObjUiParent); lv_obj_set_size(appObj_canvas_p,CAMERA_SHOW_WIDTH+50, CAMERA_SHOW_HEIGH+50); lv_obj_set_style_bg_opa(appObj_canvas_p,LV_OPA_0,LV_STATE_DEFAULT); lv_obj_set_style_border_width(appObj_canvas_p,0,LV_STATE_DEFAULT); lv_obj_set_style_radius(appObj_canvas_p,0,LV_STATE_DEFAULT); lv_obj_align(appObj_canvas_p, LV_ALIGN_CENTER, 0, 0); lv_obj_t* appObj=lv_label_create(appObj_canvas_p); HandleObjAddStyle(appObj,APP_FONT_L_24,LV_STATE_DEFAULT); lv_label_set_text(appObj,"hello world 测试中文 !!!"); lv_obj_center(appObj); }
摄像头显示
static void timerCameraShow(lv_timer_t* t){ if(timer==NULL){ return; } // 摄像头实时信息获取 if(AppCtl(CMD_APP_FACE_CONTROL_CHECK_CAMERA_DATA,0,NULL)==0 && appObjCamera!=NULL){ if(AppCtl(CMD_APP_FACE_CONTROL_GET_CAMERA_DATA,0,bufCameraBigCanvas)==0){ lv_canvas_copy_buf(appObjCamera,bufCameraBigCanvas,0,0,CAM_FACE_WIDTH, CAM_FACE_HEIGHT); lv_obj_invalidate(appObjCamera); } } }
实现效果
//
标签:LV,canvas,可行,obj,lv,240,appObj,LVGL,摄像头 From: https://www.cnblogs.com/RYSBlog/p/18375706