k230 按键拍照后,将摄像头拍照的1920*1080分辨率的图片以jpg文件格式,保存到板载TF存储卡的指定文件夹目录中
这是一个比较综合性的完整例程,演示了01 studio 的k230 canMV开发板,在通过板载的按键拍照后,将摄像头拍照的1920*1080分辨率的图片以jpg文件格式,保存到板载TF存储卡的指定文件夹目录中。
主要功能点:
一、配置摄像头高清显示的参数:
# set chn2 output format
sensor.set_framesize(width = 1920, height = 1080, chn = CAM_CHN_ID_2)
sensor.set_pixformat(Sensor.RGB565, chn = CAM_CHN_ID_2)#RGB565格式才能调用save直接存储 。
二、检测拍照按键按下的状态,以触发保存照片功能:
#按键处理(检测上升沿)
current_time = time.ticks_ms()
button_state = button.value()
if button_state == 0 and button_last_state == 1: # 下降沿
if current_time - last_press_time > debounce_delay:
# LED闪烁提示
print(f"拍照快门按键按下!")
LED.high() # 熄灭LED
time.sleep_ms(20)
LED.low() # 点亮LED
# 拍照并保存
image_count += 1
filename = f"{image_folder}/image_{image_count:05d}_{img.width()}x{img.height()}.jpg"
print(f"[INFO] 拍照保存 -> {filename}")
# 直接调用自定义的图片保存函数
wxl_save_jpg(img, filename, quality=99)
last_press_time = current_time
button_last_state = button_state
三、保存照片:
img.save(filename,quality=quality)#ok img.save(filename,quality=quality)
四、照片名称管理:
# 统计当前目录下以 “image_XX.jpg” 命名的文件数量,自动从最大编号继续
image_count = 0
existing_images = [fname for fname in os.listdir(image_folder)
if fname.startswith("image_") and fname.endswith(".jpg")]
if existing_images:
# 提取编号并找出最大值
numbers = []
for fname in existing_images:
# 假设文件名格式为 "image_XX.jpg"
# 取中间 XX 部分转为数字
try:
num_part = fname[6:11] # "image_" 长度为6,取到 ".jpg" 前还要注意下标
numbers.append(int(num_part))
except:
pass
if numbers:
image_count = max(numbers)
避坑点:
摄像头的RGB565格式才能调用save直接存储 ;
sensor.set_pixformat(Sensor.RGB565, chn = CAM_CHN_ID_2)
RGB888可显示,但无法保存 ,如果设置 RGB888格式进行图片保存的话,
sensor.set_pixformat(Sensor.RGB888, chn = CAM_CHN_ID_2)
会提示以下错误:
出现异常 'current format not support save function!'
程序实际运行效果:
程序保存照片效果:
程序源代码:
# Camera Example
#
# Note: You will need an SD card to run this example.
#
# You can start camera preview and capture yuv image.
import time, os, sysfrom media.sensor import *
from media.display import *
from media.media import *# save image raw data, use 7yuv to previewsensor_id = 2
sensor = Nonepicture_width = 1920/2
picture_height = 1080/2def save_img(img, chn):if img.format() == image.YUV420:suffix = "yuv420sp"elif img.format() == image.RGB888:suffix = "rgb888"elif img.format() == image.RGBP888:suffix = "rgb888p"else:suffix = "unkown"filename = f"/data/camera_chn_{chn:02d}_{img.width()}x{img.height()}.{suffix}"print("save capture image to file:", filename)img.save(filename)def wxl_save_jpg(img, filename, quality=95):"""将图像压缩成JPEG后写入文件 (不依赖第一段 save_jpg/MediaManager.convert_to_jpeg 的写法):param img: 传入的图像对象 (Sensor.snapshot() 得到):param filename: 保存的目标文件名 (含路径):param quality: 压缩质量 (1-100)"""#compressed_data = img.compress(quality=quality)#with open(filename, "wb") as f:# f.write(compressed_data)img.save(filename,quality=quality)#ok img.save(filename,quality=quality)print(f"[INFO] 使用 WXL_save_jpg() 保存完毕: {filename}")# ========== 自动创建图片保存文件夹 & 计算已有图片数量 ==========
image_folder = "/data/wxlimages"
# 若不存在该目录则创建
try:os.stat(image_folder) # 尝试获取目录信息
except OSError:os.mkdir(image_folder) # 若失败则创建该目录# ========== GPIO/按键/LED相关模块 ==========
from machine import Pin
from machine import FPIOA
#将GPIO52、GPIO21配置为普通GPIO模式
fpioa = FPIOA()
fpioa.set_function(52,FPIOA.GPIO52)
fpioa.set_function(21,FPIOA.GPIO21)
# ========== 初始化按键:按下时高电平 ==========
button = Pin(21, Pin.IN, Pin.PULL_UP)#构建KEY对象
debounce_delay = 200 # 按键消抖时长(ms)
last_press_time = 0
button_last_state = 0LED=Pin(52,Pin.OUT) #构建LED对象,开始熄灭 LED.value(state) #LED状态翻转state=0 #LED引脚状态# 统计当前目录下以 “image_XX.jpg” 命名的文件数量,自动从最大编号继续
image_count = 0
existing_images = [fname for fname in os.listdir(image_folder)if fname.startswith("image_") and fname.endswith(".jpg")]if existing_images:# 提取编号并找出最大值numbers = []for fname in existing_images:# 假设文件名格式为 "image_XX.jpg"# 取中间 XX 部分转为数字try:num_part = fname[6:11] # "image_" 长度为6,取到 ".jpg" 前还要注意下标numbers.append(int(num_part))except:passif numbers:image_count = max(numbers)try:print("camera_snapshot_and_save_test")#find sensor gc2093_csi2, type 30, output 1920x1080@60# 构造一个具有默认配置的摄像头对象sensor = Sensor(id=sensor_id,width=1920, height=1080)#sensor = Sensor()# sensor resetsensor.reset()# set hmirror# sensor.set_hmirror(False)# sensor vflip# sensor.set_vflip(False)# set chn0 output size, 1920x1080sensor.set_framesize(Sensor.FHD)# set chn0 output formatsensor.set_pixformat(Sensor.YUV420SP)#sensor.set_pixformat(Sensor.YUV420SP) RGB565# bind sensor chn0 to display layer video 1bind_info = sensor.bind_info()Display.bind_layer(**bind_info, layer = Display.LAYER_VIDEO2)#Display.bind_layer(**bind_info, layer = Display.LAYER_VIDEO1) 也可以设置为LAYER_VIDEO2 , 不可设置为LAYER_OSD0,会提示:'osd layer not support pix_format (31)'# set chn1 output formatsensor.set_framesize(width = 1920, height = 1080, chn = CAM_CHN_ID_1)# sensor.set_framesize(width = 640, height = 480, chn = CAM_CHN_ID_1)sensor.set_pixformat(Sensor.RGB888, chn = CAM_CHN_ID_1)# set chn2 output formatsensor.set_framesize(width = 1920, height = 1080, chn = CAM_CHN_ID_2)#sensor.set_framesize(width = 640, height = 480, chn = CAM_CHN_ID_2)#sensor.set_pixformat(Sensor.RGBP888, chn = CAM_CHN_ID_2)sensor.set_pixformat(Sensor.RGB565, chn = CAM_CHN_ID_2)#RGB565格式才能调用save直接存储 ; RGB888可显示,但无法保存 ;RGBP888和YUV420SP直接无法显示# use hdmi as display outputDisplay.init(Display.LT9611, to_ide = True, osd_num=1)# init media managerMediaManager.init()# sensor start runsensor.run()#必须在MediaManager.init()之前调用print("[INFO] 摄像头已启动,进入主循环 ...")clock = time.clock()while True:os.exitpoint()#更新当前时间(毫秒)clock.tick()#fps.tick()# 捕获通道0的图像#img = sensor.snapshot(chn=CAM_CHN_ID_1)#LAYER_OSD0 CAM_CHN_ID_0#img = sensor.snapshot()##拍摄一张图 id: CSI输入号: 默认值CSI2,开发板上的摄像头img = sensor.snapshot(chn = CAM_CHN_ID_1)#'current format not support save function!'Display.show_image(img, alpha = 128)img = sensor.snapshot(chn = CAM_CHN_ID_2)Display.show_image(img, x=0, layer = Display.LAYER_OSD1)#LAYER_OSD1 LAYER_VIDEO1 OSD序号需要和初始化OSD序号对应:Display.init(Display.LT9611, to_ide = True, osd_num=2)#Display.show_image(img, x=int(1920-800), alpha = 150,layer = Display.LAYER_OSD1)#Display.show_image(img,layer = Display.LAYER_OSD1)#LAYER_OSD1 LAYER_VIDEO1 OSD序号需要和初始化OSD序号对应:Display.init(Display.LT9611, to_ide = True, osd_num=2)# 图像处理放到这里#--------开始--------# 这里可以插入各种图像处理逻辑,例如二值化、直方图均衡化、滤波等# 当前示例仅仅直接显示原图,不做任何操作#按键处理(检测上升沿)current_time = time.ticks_ms()button_state = button.value()if button_state == 0 and button_last_state == 1: # 下降沿if current_time - last_press_time > debounce_delay:# LED闪烁提示print(f"拍照快门按键按下!")LED.high() # 熄灭LEDtime.sleep_ms(20)LED.low() # 点亮LED# 拍照并保存image_count += 1filename = f"{image_folder}/image_{image_count:05d}_{img.width()}x{img.height()}.jpg"print(f"[INFO] 拍照保存 -> {filename}")# 直接调用自定义的 lckfb_save_jpg() 函数wxl_save_jpg(img, filename, quality=99)last_press_time = current_timebutton_last_state = button_state#--------结束--------# 打印帧率到控制台print("fps = ", clock.fps())time.sleep_ms(20)except KeyboardInterrupt as e:print(f"用户停止")
except BaseException as e:print(f"出现异常 '{e}'")
finally:# sensor stop run#if isinstance(sensor, Sensor):if 'sensor' in locals() and isinstance(sensor, Sensor):sensor.stop()#必须在MediaManager.deinit()之前调用# deinit displayDisplay.deinit()os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)time.sleep_ms(100)# release media bufferMediaManager.deinit()'''# drop 100 frames
for i in range(500):sensor.snapshot()# snapshot and save
img = sensor.snapshot(chn = CAM_CHN_ID_0)
save_img(img, 0)img = sensor.snapshot(chn = CAM_CHN_ID_1)
save_img(img, 1)img = sensor.snapshot(chn = CAM_CHN_ID_2)
save_img(img, 2)camera_snapshot_and_save_test
find sensor gc2093_csi2, type 30, output 1920x1080@60
vb common pool count 6
sensor(0), mode 0, buffer_num 4, buffer_size 0
[INFO] 摄像头已启动,进入主循环 ...
fps = 22.2222
fps = 27.3973
fps = 30.303
fps = 33.0578
fps = 34.4828
fps = 35.2941
fps = 34.6535
fps = 35.5556
fps = 36.4372
fps = 35.8423
fps = 35.4839
fps = 36.2538
fps = 36.3128
fps = 35.8056
fps = 36.3196
fps = 36.6972
fps = 36.2473
fps = 36.6599
fps = 36.965
fps = 36.4963
fps = 36.0825
fps = 36.4238
fps = 36.566
fps = 36.3086
fps = 36.6032
fps = 36.7751
fps = 36.4372
fps = 37.1653
fps = 37.1304
拍照快门按键按下!
[INFO] 拍照保存 -> /data/wxlimages/image_00001_1920x1080.jpg
[INFO] 使用 WXL_save_jpg() 保存完毕: /data/wxlimages/image_00001_1920x1080.jpg
fps = 30.0074
fps = 30.0037
fps = 30.0164
fps = 30.0725
fps = 34.4886
拍照快门按键按下!
[INFO] 拍照保存 -> /data/wxlimages/image_00002_1920x1080.jpg
[INFO] 使用 WXL_save_jpg() 保存完毕: /data/wxlimages/image_00002_1920x1080.jpg
fps = 31.3501
fps = 31.369
fps = 31.383
fps = 31.4065
fps = 31.4203'''