当前位置: 首页 > ds >正文

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'''

http://www.xdnf.cn/news/19357.html

相关文章:

  • 营业执照经营范围行业提取工具库项目方案解读(php封装库)
  • 项目管理在企业中的作用
  • Python 多线程日志错乱:logging.Handler 的并发问题
  • 什么是IO多路复用
  • ESPTimer vs GPTimer:ESP32 定时器系统深度解析
  • 【Java基础知识 19】继承
  • Spring注解演进与自动装配原理深度解析:从历史发展到自定义Starter实践
  • 197-200CSS3响应式布局,BFC
  • 内存管理(智能指针,内存对齐,野指针,悬空指针)
  • 时间轴组件开发:实现灵活的时间范围选择
  • PHP单独使用phinx使用数据库迁移
  • Spring Cloud微服务架构设计与实战:从组件落地到分布式事务解决
  • 精简版UDP网络编程:Socket套接字应用
  • 链表有环找入口节点原理
  • css绘制三角形
  • A股大盘数据-20250829 分析
  • C++基础(③反转字符串(字符串 + 双指针))
  • 阿里巴巴拍立淘API返回值解析与商品信息优化指南
  • 刷题日记0829
  • Libvio 访问异常排查指南
  • OpenEuler部署LoganaLyzer
  • linux实时性研究
  • Python 编码与加密全解析:从字符编码到 RSA 签名验证
  • Win11 压缩实测:Win11 的压缩软件的最佳配置和使用方式
  • 龙迅#LT7621GX适用于两路HDMI2.1/DP1.4A转HDMI2.1混切应用,分辨率高达8K60HZ!
  • Anaconda安装与conda使用详细版
  • Linux系统编程—进程概念
  • 文本嵌入模型的本质
  • 进程与线程的根本区别
  • Parasoft赋能测试:精准捕捉运行时缺陷