YUV422 to YUV420sp color invert

最近在做camera模块,出现了几个bug很有意思。
 
1. 颜色翻转,红色 变成了 蓝绿色。分析后是YUV422 to YUV420SP的时候,算法出现问题。后来把cb,cr互换了一下,颜色就对了。真的很奇怪。
 
2. 转换video quality的时候,从high quality 切换到 low quality的时候,camera报错了。后来发现是分辨率不一样。


import time, os, sys, math from media.sensor import * from media.display import * from media.media import * from machine import PWM, FPIOA sensor = None fps = time.clock() os.exitpoint(os.EXITPOINT_ENABLE) # 初始化退出点检测 # 屏幕参数 SCREEN_WIDTH = 320 # QVGA宽度 SCREEN_HEIGHT = 240 # QVGA高度 SCREEN_CENTER_X = SCREEN_WIDTH // 2 SCREEN_CENTER_Y = SCREEN_HEIGHT // 2 # 中心区域参数 CENTER_ZONE_RATIO = 0.4 # 中心区域占屏幕的比例 CENTER_ZONE_WIDTH = int(SCREEN_WIDTH * CENTER_ZONE_RATIO) CENTER_ZONE_HEIGHT = int(SCREEN_HEIGHT * CENTER_ZONE_RATIO) CENTER_ZONE_X = (SCREEN_WIDTH - CENTER_ZONE_WIDTH) // 2 CENTER_ZONE_Y = (SCREEN_HEIGHT - CENTER_ZONE_HEIGHT) // 2 # A4纸长宽比参数 A4_RATIO = 1.4142 RATIO_TOLERANCE = 0.08 # 舵机参数 HORIZONTAL_MAX_ANGLE = 270 # 水平舵机最大角度(度) VERTICAL_MAX_ANGLE = 180 # 垂直舵机最大角度(度) HORIZONTAL_CENTER = 135 # 水平中心位置 VERTICAL_CENTER = 90 # 垂直中心位置 # 初始化FPIOA fpioa = FPIOA() fpioa.set_function(47, FPIOA.PWM3) # 水平舵机 -> PWM3 (引脚47) fpioa.set_function(46, FPIOA.PWM2) # 垂直舵机 -> PWM2 (引脚46) # 初始化PWM(频率50Hz) pwm_lr = PWM(3, freq=50, duty=0) # 通道3对应PWM3 pwm_ud = PWM(2, freq=50, duty=0) # 通道2对应PWM2 # 舵机控制模块 PID class PID: def __init__(self, kp, ki, kd, max_i=1000, max_out=10): self.kp = kp #静态参数 self.ki = ki self.kd = kd self.max_i = max_i # 积分限幅 self.max_out = max_out # 输出限幅 self.error_sum = 0 #当前误差 self.last_error = 0 #之前误差 def compute(self, target, current): # 计算PID输出 error = target - current # P项 p_term = self.kp * error # I项(带限幅) self.error_sum += error self.error_sum = max(-self.max_i, min(self.error_sum, self.max_i)) i_term = self.ki * self.error_sum # D项 d_term = self.kd * (error - self.last_error) self.last_error = error # 总和并限幅 output = p_term + i_term + d_term return max(-self.max_out, min(output, self.max_out)) # PID参数调整 pid_lr = PID(0.05, 0.0005, 0.03) # 水平方向PID pid_ud = PID(0.05, 0.0005, 0.03) # 垂直方向PID # 指数移动平均滤波器 class EMAFilter: def __init__(self, alpha=0.2): self.alpha = alpha self.value = None def update(self, new_value): if self.value is None: self.value = new_value else: self.value = self.alpha * new_value + (1 - self.alpha) * self.value return self.value # 滤波强度调整(值越小越平滑) filter_x = EMAFilter(0.15) filter_y = EMAFilter(0.15) def angle_to_duty(angle, max_angle): # 将角度转换为占空比(0-100%) pulse_min = 500 # 0°对应500us pulse_max = 2500 # max_angle对应2500us pulse_width = pulse_min + (pulse_max - pulse_min) * angle / max_angle return max(2.5, min(duty_cycle, 12.5)) # 确保在安全范围内 def set_servo_angles(angle_h, angle_v): # 设置舵机角度(带限幅保护) angle_h = max(0, min(angle_h, HORIZONTAL_MAX_ANGLE)) angle_v = max(0, min(angle_v, VERTICAL_MAX_ANGLE)) pwm_lr.duty(angle_to_duty(angle_h, HORIZONTAL_MAX_ANGLE)) pwm_ud.duty(angle_to_duty(angle_v, VERTICAL_MAX_ANGLE)) try: # 初始化传感器 sensor = Sensor() sensor.reset() # 主通道配置 (800x480 YUV420SP) sensor.set_framesize(width = 800, height = 480) sensor.set_pixformat(Sensor.YUV420SP) bind_info = sensor.bind_info() Display.bind_layer(**bind_info, layer=Display.LAYER_VIDEO1) # 副通道配置 (QVGA 320x240 RGB565) sensor.set_framesize(Sensor.QVGA, chn=CAM_CHN_ID_2) sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_2) # 初始化显示和媒体系统 Display.init(Display.ST7701) MediaManager.init() sensor.run() fps = time.clock() sensor.set_hmirror(True) sensor.set_vflip(True) sensor.skip_frames(500) # 跳过初始帧稳定摄像头 # 初始化舵机位置 current_angle_h = HORIZONTAL_CENTER # 水平居中 current_angle_v = VERTICAL_CENTER # 垂直居中 set_servo_angles(current_angle_h, current_angle_v) # 目标跟踪状态 target_lost_count = 0 MAX_TARGET_LOST = 10 # 连续10帧丢失目标后复位 clock = time.clock() last_detection_time = time.time() # 主循环 while True: fps.tick() # 更新帧率计时 os.exitpoint() # 检查退出信号 clock.tick() # 从副通道获取RGB565图像 img = sensor.snapshot(chn=CAM_CHN_ID_2) # 绘制屏幕中心点 img.draw_circle(SCREEN_CENTER_X, SCREEN_CENTER_Y, 8, color=(255, 255, 0), thickness=1) # 黄色实心圆点 # 高效灰度转换与二值化(合并操作) gray = img.to_grayscale(copy=False) # 避免复制新图像 gray.binary([(0, 40)], invert=False) # 直接操作原灰度图,调整阈值范围,适应不同光照 # 形态学优化(增强连通性) gray.dilate(2) # 膨胀填补小孔 gray.erode(1) # 腐蚀平滑边缘 # 在图像中查找矩形 rects = img.find_rects(threshold = 60000) #精度 # 矩形处理逻辑 valid_rects = [] for rect in rects: x, y, w, h = rect.rect() center_x, center_y = x + w//2, y + h//2 area = w * h # 长宽比检测(防除零) if min(w, h) > 10: current_ratio = max(w, h) / min(w, h) is_a4 = abs(current_ratio - A4_RATIO) < RATIO_TOLERANCE else: continue # 中心区域检测 in_center = (CENTER_ZONE_X <= center_x <= CENTER_ZONE_X + CENTER_ZONE_WIDTH and CENTER_ZONE_Y <= center_y <= CENTER_ZONE_Y + CENTER_ZONE_HEIGHT) if is_a4 and in_center: a4_detected = True last_detection_time = time.time() valid_rects.append(rect) # 仅缓存有效矩形 # 绘制:蓝色(1, 147, 230)绿色(0, 255, 0)红色(255, 0, 0) img.draw_line(SCREEN_CENTER_X, SCREEN_CENTER_Y, center_x, center_y, color=(0, 255, 255), thickness=3) # 矩形中心到屏幕中心的青色连线 img.draw_rectangle(rect.rect(), color=(1, 147, 230), thickness=5)# 蓝色矩形框 img.draw_circle(center_x, center_y, 10, color=(0, 255, 0), thickness=0) #绿色中心点 print(f"矩形中心({center_x}, {center_y})") # 舵机控制逻辑 if is_a4: a4_detected = True target_lost_count = 0 last_detection_time = time.time() # 计算偏移误差(像素) error_x = center_x - SCREEN_CENTER_X error_y = center_y - SCREEN_CENTER_Y # 应用滤波 filtered_error_x = filter_x.update(error_x) filtered_error_y = filter_y.update(error_y) # 计算PID调整量,注意:舵机方向需要根据实际安装调整符号 adj_x = pid_lr.compute(0, filtered_error_x) adj_y = pid_ud.compute(0, filtered_error_y) # 更新舵机角度 current_angle_h -= adj_x # 根据实际安装调整符号 current_angle_v += adj_y # 根据实际安装调整符号 # 限幅保护 current_angle_h = max(0, min(current_angle_h, HORIZONTAL_MAX_ANGLE)) current_angle_v = max(0, min(current_angle_v, VERTICAL_MAX_ANGLE)) # 设置舵机位置 set_servo_angles(current_angle_h, current_angle_v) # 目标丢失处理 if not a4_detected: target_lost_count += 1 if target_lost_count > MAX_TARGET_LOST: # 缓慢返回中心位置 adj_x = pid_lr.compute(0, SCREEN_CENTER_X - current_angle_h) adj_y = pid_ud.compute(0, SCREEN_CENTER_Y - current_angle_v) current_angle_h += adj_x * 0.1 current_angle_v += adj_y * 0.1 set_servo_angles(current_angle_h, current_angle_v) # 完全复位 if target_lost_count > MAX_TARGET_LOST * 2: current_angle_h = HORIZONTAL_CENTER current_angle_v = VERTICAL_CENTER set_servo_angles(current_angle_h, current_angle_v) target_lost_count = 0 # 将处理后的图像显示在OSD层 (屏幕右侧) Display.show_image(img, x=800-320, layer=Display.LAYER_OSD1) except KeyboardInterrupt as e: print("用户中断:", e) except BaseException as e: print(f"异常: {e}") finally: # 清理资源 if isinstance(sensor, Sensor): sensor.stop() Display.deinit() MediaManager.deinit() os.exitpoint(os.EXITPOINT_ENABLE_SLEEP) time.sleep_ms(100) 你是一个对can mv K230代码非常了解的工程师,请帮我找出这个代码中的潜在问题,并合理修改代码,告诉我如何修改以及修改原因
最新发布
08-03
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值