K230图像裁剪后总是报错:ValueError: width must be an integral multiple of 8 pixels,但是再裁剪之前已判断是否为8的倍数,也做了处理:对8整除再乘八

Viewed 37

重现步骤

期待结果和实际结果

软硬件版本信息

8.1最新版的固件库,嘉立创K230

import time, os, sys, gc
from machine import Pin
from media.sensor import *     # 摄像头接口 / Camera interface
from media.display import *    # 显示接口 / Display interface
from media.media import *      # 媒体资源管理器 / Media manager
from machine import UART
from machine import FPIOA
import _thread
import cv_lite                 # cv_lite扩展模块 / cv_lite extension module
import ulab.numpy as np
import math

image_shape = [480, 800]

sensor = Sensor(id=2, width=1280, height=720,fps=90)
sensor.reset()
sensor.set_framesize(width=image_shape[1], height=image_shape[0])
sensor.set_pixformat(Sensor.RGB888)

#Display.init(Display.VIRT, width=image_shape[1], height=image_shape[0], to_ide=True, quality=50)
Display.init(Display.ST7701, width=800, height=480, to_ide=True,osd_num=2)
# -------------------------------
# 初始化媒体资源管理器并启动摄像头 / Init media manager and start camera
# -------------------------------
MediaManager.init()
sensor.run()

# 创建FPIOA对象,用于初始化引脚功能配置
fpioa = FPIOA()

# 设置引脚功能,将指定的引脚配置为普通GPIO功能
fpioa.set_function(53, FPIOA.GPIO53)

# 按键引脚为53,按下时高电平,设置为输入模式
button = Pin(53, Pin.IN, Pin.PULL_DOWN)  # 使用下拉电阻

# -------------------------------
# 启动帧率计时器 / Start FPS timer
# -------------------------------
clock = time.clock()

# 相机参数
FOCAL_LENGTH_MM = 3.0  # 焦距3.0mm
SENSOR_WIDTH_MM = 4.8  # 假设传感器宽度4.8mm (典型值)
IMAGE_WIDTH_PIXELS = image_shape[1]  # 图像宽度(像素)

# 计算焦距的像素当量
focal_length_pixels = (FOCAL_LENGTH_MM * IMAGE_WIDTH_PIXELS) / SENSOR_WIDTH_MM

# 矩形实际物理尺寸
ACTUAL_WIDTH_MM = 210.0   # 实际宽度(毫米)
ACTUAL_HEIGHT_MM = 297.0  # 实际高度(毫米)

def detect_corners(cor_img):
    if rect_w<cor_img.width() and rect_h<cor_img.height() and rect_w%8==0 and rect_h%8==0:
        cor_img = cor_img.copy(roi=(rect_x,rect_y,rect_w,rect_h))
    else:
        cor_img=cor_img
    cor_img_shape=[cor_img.height(), cor_img.width()]
    cor_img_np = cor_img.to_numpy_ref()  # 获取 GRAYSCALE ndarray 引用 / Get GRAYSCALE ndarray reference
    max_corners       = 20        # 最大角点数 / Maximum number of corners
    quality_level     = 0.3      # Shi-Tomasi质量因子 / Corner quality factor (0.01 ~ 0.1)
    min_distance      = 8.0      # 最小角点距离 / Minimum distance between corners
    corners = cv_lite.grayscale_find_corners(
        cor_img_shape, cor_img_np,
        max_corners,
        quality_level,
        min_distance
    )

    # 遍历角点数组,绘制角点 / Draw detected corners
    for i in range(0, len(corners), 2):
        x = corners[i]
        y = corners[i + 1]
        img_cut.draw_circle(x+rect_x, y+rect_y, 3, color=(255, 255, 255), fill=True)
    Display.show_image(cor_img, x=int((800-img.width())/2),y=int((480-img.height())/2),layer=Display.LAYER_OSD2)


def find_target_rectangle(img_):
    """在图像中查找矩形目标并返回边界框尺寸(返回面积最大的矩形)"""
    # 查找矩形
    binary=img_.binary([(0, 74)])
    img_.gaussian(1)
    edge=img_.laplacian(1,mul=0.2)  # 拉普拉斯边缘检测,窗口大小为 3
    edge.binary([(0, 5)])

    rects = edge.find_rects(threshold=1000)

    max_area = 0
    target_rect = None

    for rect in rects:
        w = rect.w()
        h = rect.h()

        # 过滤小区域
        if w < 40 or h < 50:
            continue

        # 计算宽高比 (210/297 ≈ 0.707)
        aspect_ratio = min(w, h) / max(w, h)
        if 0.6 < aspect_ratio < 0.8:
             # 计算当前矩形面积
            area = w * h
             # 更新最大面积矩形
            if area > max_area:
                max_area = area
                target_rect = rect

    return target_rect


def calculate_distance(rect):
    """
    根据检测到的矩形计算到目标的距离
    参数:
        rect - 检测到的矩形对象
    返回:
        距离(毫米), 如果矩形无效返回0
    """
    if rect is None:
        return 0

    w = rect.w()
    h = rect.h()

    # 过滤无效尺寸
    if w <= 0 or h <= 0:
        return 0

    # 计算距离(使用宽度和高度分别计算后取平均)
    distance_width = (ACTUAL_WIDTH_MM * focal_length_pixels) / w
    distance_height = (ACTUAL_HEIGHT_MM * focal_length_pixels) / h

    return (distance_width + distance_height) / 2.0

while True:
    clock.tick()

    # 拍摄当前帧图像 / Capture current frame
    img = sensor.snapshot()
    img.histeq()
    img_cut=img.copy(roi=(240,0,296,480))
    img_gray=img.copy(roi=(240,0,296,480))
    img_gray=img_gray.to_grayscale()

    rect = find_target_rectangle(img_gray)
    if rect:
        # 获取矩形属性
        x = rect.x()
        y = rect.y()
        w = rect.w()
        h = rect.h()

        # 裁剪矩形内部区域
        rect_offset = 0.1  # 边界偏移比例
        rect_x = max(0, int(x + w * rect_offset))
        rect_y = max(0, int(y + h * rect_offset))
        rect_w = max(8, int(w * (1 - 2 * rect_offset)))
        rect_h = max(8, int(h * (1 - 2 * rect_offset)))
        if ((rect_w+7)//8)*8!=0 or((rect_h+7)//8)*80:
            rect_w = ((rect_w+7)//8)*8
            rect_h = ((rect_h+7)//8)*8
            detect_corners(img_gray)
        else:
            rect_w = 160
            rect_h = 160
        corner=rect.corners()
        """
        img_cut.draw_line(corner[0][0], corner[0][1], corner[1][0], corner[1][1], color=(0, 255, 0), thickness=1)
        img_cut.draw_line(corner[2][0], corner[2][1], corner[1][0], corner[1][1], color=(0, 255, 0), thickness=1)
        img_cut.draw_line(corner[2][0], corner[2][1], corner[3][0], corner[3][1], color=(0, 255, 0), thickness=1)
        img_cut.draw_line(corner[0][0], corner[0][1], corner[3][0], corner[3][1], color=(0, 255, 0), thickness=1)
        """
        # 在图像上绘制矩形
        img_cut.draw_rectangle(x, y, w, h, color=(0, 0, 255), thickness=2)
        print(w,h,calculate_distance(rect))


    # 显示图像 / Display image with corners
    Display.show_image(img, layer=Display.LAYER_OSD0)
    Display.show_image(img_cut,x=800-img_gray.width(),layer=Display.LAYER_OSD1)

    # 进行垃圾回收 / Perform garbage collection
    gc.collect()


# -------------------------------
# 退出时释放资源 / Cleanup on exit
# -------------------------------
sensor.stop()
Display.deinit()
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
time.sleep_ms(100)
MediaManager.deinit()

错误日志

尝试解决过程

补充材料

1 Answers

你好,可以参考这个, 主要是保证送给显示得图片(cor_img)一定要是8对齐得。

import time, os, sys, gc
from machine import Pin
from media.sensor import *     # 摄像头接口 / Camera interface
from media.display import *    # 显示接口 / Display interface
from media.media import *      # 媒体资源管理器 / Media manager
from machine import UART
from machine import FPIOA
import _thread
import cv_lite                 # cv_lite扩展模块 / cv_lite extension module
import ulab.numpy as np
import math

image_shape = [480, 800]

def calculate_crop(sensor_width, sensor_height, target_width, target_height):
    """
    Calculate center crop rectangle from sensor resolution to match target resolution
    with preserved aspect ratio.

    Returns:
        (crop_x, crop_y, crop_width, crop_height)
    """
    scale = min(sensor_width // target_width, sensor_height // target_height)
    crop_width = int(target_width * scale)
    crop_height = int(target_height * scale)
    crop_x = (sensor_width - crop_width) // 2
    crop_y = (sensor_height - crop_height) // 2
    return (crop_x, crop_y, crop_width, crop_height)

# -------------------------------
# 初始化摄像头(灰度图模式) / Initialize camera (grayscale mode)
# -------------------------------
sensor = Sensor(id=2, fps = 90)
sensor.reset()

sensor_width = sensor.width(None)
sensor_height = sensor.height(None)
sensor.set_framesize(width=image_shape[1], height=image_shape[0], crop = calculate_crop(sensor_width,sensor_height,image_shape[1],image_shape[0]))
sensor.set_pixformat(Sensor.RGB888)

#Display.init(Display.VIRT, width=image_shape[1], height=image_shape[0], to_ide=True, quality=50)
Display.init(Display.ST7701, width=800, height=480, to_ide=True,osd_num=2)
# -------------------------------
# 初始化媒体资源管理器并启动摄像头 / Init media manager and start camera
# -------------------------------
MediaManager.init()
sensor.run()

# 创建FPIOA对象,用于初始化引脚功能配置
fpioa = FPIOA()

# 设置引脚功能,将指定的引脚配置为普通GPIO功能
fpioa.set_function(53, FPIOA.GPIO53)

# 按键引脚为53,按下时高电平,设置为输入模式
button = Pin(53, Pin.IN, Pin.PULL_DOWN)  # 使用下拉电阻

# -------------------------------
# 启动帧率计时器 / Start FPS timer
# -------------------------------
clock = time.clock()

# 相机参数
FOCAL_LENGTH_MM = 3.0  # 焦距3.0mm
SENSOR_WIDTH_MM = 4.8  # 假设传感器宽度4.8mm (典型值)
IMAGE_WIDTH_PIXELS = image_shape[1]  # 图像宽度(像素)

# 计算焦距的像素当量
focal_length_pixels = (FOCAL_LENGTH_MM * IMAGE_WIDTH_PIXELS) / SENSOR_WIDTH_MM

# 矩形实际物理尺寸
ACTUAL_WIDTH_MM = 210.0   # 实际宽度(毫米)
ACTUAL_HEIGHT_MM = 297.0  # 实际高度(毫米)

def detect_corners(cor_img, rect_x, rect_y, rect_w, rect_h):
    print(cor_img)
    if rect_w<cor_img.width() and rect_h<cor_img.height() and rect_w%8==0 and rect_h%8==0:
        # Optionally clamp width to image boundary
        max_w = cor_img.width() - rect_x
        aligned_w = min(rect_w, max_w)

        aligned_w = aligned_w & ~7

        # Copy the ROI with aligned width
        cor_img = cor_img.copy(roi=(rect_x, rect_y, aligned_w, rect_h))

        print(f"copy {rect_x},{rect_y},{rect_w},{rect_h} {aligned_w}")
    else:
        cor_img=cor_img
        print("no copy")
    cor_img_shape=[cor_img.height(), cor_img.width()]
    cor_img_np = cor_img.to_numpy_ref()  # 获取 GRAYSCALE ndarray 引用 / Get GRAYSCALE ndarray reference
    max_corners       = 20        # 最大角点数 / Maximum number of corners
    quality_level     = 0.3      # Shi-Tomasi质量因子 / Corner quality factor (0.01 ~ 0.1)
    min_distance      = 8.0      # 最小角点距离 / Minimum distance between corners
    corners = cv_lite.grayscale_find_corners(
        cor_img_shape, cor_img_np,
        max_corners,
        quality_level,
        min_distance
    )

    # 遍历角点数组,绘制角点 / Draw detected corners
    for i in range(0, len(corners), 2):
        x = corners[i]
        y = corners[i + 1]
        img_cut.draw_circle(x+rect_x, y+rect_y, 3, color=(255, 255, 255), fill=True)
    print(cor_img)
    Display.show_image(cor_img, x=int((800-img.width())/2),y=int((480-img.height())/2),layer=Display.LAYER_OSD2)


def find_target_rectangle(img_):
    """在图像中查找矩形目标并返回边界框尺寸(返回面积最大的矩形)"""
    # 查找矩形
    binary=img_.binary([(0, 74)])
    img_.gaussian(1)
    edge=img_.laplacian(1,mul=0.2)  # 拉普拉斯边缘检测,窗口大小为 3
    edge.binary([(0, 5)])

    rects = edge.find_rects(threshold=1000)

    max_area = 0
    target_rect = None

    for rect in rects:
        w = rect.w()
        h = rect.h()

        # 过滤小区域
        if w < 40 or h < 50:
            continue

        # 计算宽高比 (210/297 ≈ 0.707)
        aspect_ratio = min(w, h) / max(w, h)
        if 0.6 < aspect_ratio < 0.8:
             # 计算当前矩形面积
            area = w * h
             # 更新最大面积矩形
            if area > max_area:
                max_area = area
                target_rect = rect

    return target_rect


def calculate_distance(rect):
    """
    根据检测到的矩形计算到目标的距离
    参数:
        rect - 检测到的矩形对象
    返回:
        距离(毫米), 如果矩形无效返回0
    """
    if rect is None:
        return 0

    w = rect.w()
    h = rect.h()

    # 过滤无效尺寸
    if w <= 0 or h <= 0:
        return 0

    # 计算距离(使用宽度和高度分别计算后取平均)
    distance_width = (ACTUAL_WIDTH_MM * focal_length_pixels) / w
    distance_height = (ACTUAL_HEIGHT_MM * focal_length_pixels) / h

    return (distance_width + distance_height) / 2.0

while True:
    clock.tick()

    # 拍摄当前帧图像 / Capture current frame
    img = sensor.snapshot()
    img.histeq()
    img_cut=img.copy(roi=(240,0,296,480))
    img_gray=img.copy(roi=(240,0,296,480))
    img_gray=img_gray.to_grayscale()

    rect = find_target_rectangle(img_gray)
    if rect:
        # 获取矩形属性
        x = rect.x()
        y = rect.y()
        w = rect.w()
        h = rect.h()

        # 裁剪矩形内部区域
        rect_offset = 0.1  # 边界偏移比例
        rect_x = max(0, int(x + w * rect_offset))
        rect_y = max(0, int(y + h * rect_offset))
        rect_w = max(8, int(w * (1 - 2 * rect_offset)))
        rect_h = max(8, int(h * (1 - 2 * rect_offset)))
        if ((rect_w+7)//8)*8!=0 or((rect_h+7)//8)*80:
            rect_w = ((rect_w+7)//8)*8
            rect_h = ((rect_h+7)//8)*8
            detect_corners(img_gray, rect_x, rect_y, rect_w, rect_h)
        else:
            rect_w = 160
            rect_h = 160
        corner=rect.corners()
        """
        img_cut.draw_line(corner[0][0], corner[0][1], corner[1][0], corner[1][1], color=(0, 255, 0), thickness=1)
        img_cut.draw_line(corner[2][0], corner[2][1], corner[1][0], corner[1][1], color=(0, 255, 0), thickness=1)
        img_cut.draw_line(corner[2][0], corner[2][1], corner[3][0], corner[3][1], color=(0, 255, 0), thickness=1)
        img_cut.draw_line(corner[0][0], corner[0][1], corner[3][0], corner[3][1], color=(0, 255, 0), thickness=1)
        """
        # 在图像上绘制矩形
        img_cut.draw_rectangle(x, y, w, h, color=(0, 0, 255), thickness=2)
        print(w,h,calculate_distance(rect))


    # 显示图像 / Display image with corners
    Display.show_image(img, layer=Display.LAYER_OSD0)
    Display.show_image(img_cut,x=800-img_gray.width(),layer=Display.LAYER_OSD1)

    # 进行垃圾回收 / Perform garbage collection
    gc.collect()


# -------------------------------
# 退出时释放资源 / Cleanup on exit
# -------------------------------
sensor.stop()
Display.deinit()
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
time.sleep_ms(100)
MediaManager.deinit()