1.28. 图像裁剪、插入和缩放 -- CROP API

注意

CROP API 大部分已被更新的 SELECTION API 取代。在大多数情况下,应优先使用新的 API,但像素纵横比检测除外,它由 VIDIOC_CROPCAP 实现,并且在 SELECTION API 中没有等效项。有关两个 API 的比较,请参阅 与旧的裁剪 API 的比较

一些视频捕获设备可以对图片的子部分进行采样,并将其缩小或放大到任意大小的图像。我们将这些能力称为裁剪和缩放。一些视频输出设备可以放大或缩小图像,并将其插入到视频信号的任意扫描线和水平偏移处。

应用程序可以使用以下 API 来选择视频信号中的区域,查询默认区域和硬件限制。

注意

尽管它们的名称如此,VIDIOC_CROPCAPVIDIOC_G_CROPVIDIOC_S_CROP ioctl 适用于输入和输出设备。

缩放需要一个源和一个目标。在视频捕获或覆盖设备上,源是视频信号,裁剪 ioctl 确定实际采样的区域。目标是由应用程序读取或覆盖到图形屏幕上的图像。它们的大小(以及覆盖的位置)通过 VIDIOC_G_FMTVIDIOC_S_FMT ioctl 协商。

在视频输出设备上,源是由应用程序传入的图像,它们的大小再次通过 VIDIOC_G_FMTVIDIOC_S_FMT ioctl 协商,或者可以编码在压缩视频流中。目标是视频信号,裁剪 ioctl 确定图像插入的区域。

即使设备不支持缩放或 VIDIOC_G_CROPVIDIOC_S_CROP ioctl,也会定义源矩形和目标矩形。在这种情况下,它们的大小(以及适用的位置)将是固定的。

注意

所有支持 CROP 或 SELECTION API 的捕获和输出设备也将支持 VIDIOC_CROPCAP ioctl。

1.28.1. 裁剪结构

crop.svg

图像裁剪、插入和缩放

裁剪、插入和缩放过程

对于捕获设备,可以采样的区域的左上角坐标、宽度和高度由 bounds 子结构给出,该子结构是 VIDIOC_CROPCAP ioctl 返回的 v4l2_cropcap 结构的一部分。为了支持各种硬件,此规范未定义原点或单位。但是,按照惯例,驱动程序应相对于 0H(水平同步脉冲的前沿,请参见 图 4.1。行同步)水平计算未缩放的采样。垂直方向,第一个场的 ITU-R 行号(请参见 ITU R-525 线编号,其中 525 行625 行),如果驱动程序可以捕获两个场,则乘以二。

源矩形的左上角坐标、宽度和高度,即实际采样的区域,由结构 v4l2_crop 使用与结构 v4l2_cropcap 相同的坐标系给出。应用程序可以使用 VIDIOC_G_CROPVIDIOC_S_CROP ioctl 来获取和设置此矩形。它必须完全位于捕获边界内,并且驱动程序可以根据硬件限制进一步调整请求的大小和/或位置。

每个捕获设备都有一个默认的源矩形,由结构 v4l2_cropcapdefrect 子结构给出。此矩形的中心应与视频信号的活动画面区域的中心对齐,并覆盖驱动程序编写者认为的完整画面。驱动程序应在首次加载驱动程序时将源矩形重置为默认值,但不应在之后重置。

对于输出设备,这些结构和 ioctl 也相应地使用,定义图像将插入到视频信号中的目标矩形。

1.28.2. 缩放调整

视频硬件可能具有各种裁剪、插入和缩放限制。它可能只能放大或缩小,仅支持离散缩放因子,或者在水平和垂直方向上具有不同的缩放能力。此外,它可能根本不支持缩放。同时,结构 v4l2_crop 矩形可能必须对齐,并且源矩形和目标矩形可能具有任意的上限和下限大小限制。特别是,结构 v4l2_crop 中的最大 widthheight 可能小于结构 v4l2_cropcapbounds 区域。因此,通常情况下,驱动程序应调整请求的参数并返回实际选择的值。

应用程序可以先更改源矩形或目标矩形,因为它们可能更喜欢特定的图像大小或视频信号中的某个区域。如果驱动程序必须调整两者以满足硬件限制,则最后请求的矩形应优先,并且驱动程序应尽可能调整相反的矩形。VIDIOC_TRY_FMT ioctl 不应更改驱动程序状态,因此仅应调整请求的矩形。

假设视频捕获设备上的缩放限制为任一方向上的 1:1 或 2:1,并且目标图像大小必须是 16 × 16 像素的倍数。源裁剪矩形设置为默认值,在本例中也是上限,为 640 × 400 像素,偏移量为 0, 0。应用程序请求的图像大小为 300 × 225 像素,假设视频将相应地从“全图”缩小。驱动程序将图像大小设置为最接近的可能值 304 × 224,然后选择最接近请求大小的裁剪矩形,即 608 × 224 (224 × 2:1 将超过限制 400)。偏移量 0, 0 仍然有效,因此未修改。给定 VIDIOC_CROPCAP 报告的默认裁剪矩形,应用程序可以轻松地提出另一个偏移量来使裁剪矩形居中。

现在,应用程序可能会坚持使用更接近原始请求的图片纵横比来覆盖一个区域,因此它要求裁剪矩形为 608 × 456 像素。当前的缩放因子将裁剪限制为 640 × 384,因此驱动程序返回裁剪大小 608 × 384,并将图像大小调整为最接近的 304 × 192。

1.28.3. 示例

源矩形和目标矩形在关闭和重新打开设备时应保持不变,以便将数据传入或传出设备无需特殊准备。更高级的应用程序应确保参数在开始 I/O 之前是合适的。

注意

在接下来的两个示例中,假设使用视频捕获设备;对于其他类型的设备,请更改 V4L2_BUF_TYPE_VIDEO_CAPTURE

1.28.4. 示例:重置裁剪参数

struct v4l2_cropcap cropcap;
struct v4l2_crop crop;

memset (&cropcap, 0, sizeof (cropcap));
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

if (-1 == ioctl (fd, VIDIOC_CROPCAP, &cropcap)) {
    perror ("VIDIOC_CROPCAP");
    exit (EXIT_FAILURE);
}

memset (&crop, 0, sizeof (crop));
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect;

/* Ignore if cropping is not supported (EINVAL). */

if (-1 == ioctl (fd, VIDIOC_S_CROP, &crop)
    && errno != EINVAL) {
    perror ("VIDIOC_S_CROP");
    exit (EXIT_FAILURE);
}

1.28.5. 示例:简单缩小

struct v4l2_cropcap cropcap;
struct v4l2_format format;

reset_cropping_parameters ();

/* Scale down to 1/4 size of full picture. */

memset (&format, 0, sizeof (format)); /* defaults */

format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

format.fmt.pix.width = cropcap.defrect.width >> 1;
format.fmt.pix.height = cropcap.defrect.height >> 1;
format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;

if (-1 == ioctl (fd, VIDIOC_S_FMT, &format)) {
    perror ("VIDIOC_S_FORMAT");
    exit (EXIT_FAILURE);
}

/* We could check the actual image size now, the actual scaling factor
   or if the driver can scale at all. */

1.28.6. 示例:选择输出区域

注意

此示例假设使用输出设备。

struct v4l2_cropcap cropcap;
struct v4l2_crop crop;

memset (&cropcap, 0, sizeof (cropcap));
cropcap.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;

if (-1 == ioctl (fd, VIDIOC_CROPCAP;, &cropcap)) {
    perror ("VIDIOC_CROPCAP");
    exit (EXIT_FAILURE);
}

memset (&crop, 0, sizeof (crop));

crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
crop.c = cropcap.defrect;

/* Scale the width and height to 50 % of their original size
   and center the output. */

crop.c.width /= 2;
crop.c.height /= 2;
crop.c.left += crop.c.width / 2;
crop.c.top += crop.c.height / 2;

/* Ignore if cropping is not supported (EINVAL). */

if (-1 == ioctl (fd, VIDIOC_S_CROP, &crop)
    && errno != EINVAL) {
    perror ("VIDIOC_S_CROP");
    exit (EXIT_FAILURE);
}

1.28.7. 示例:当前缩放因子和像素纵横比

注意

此示例假设使用视频捕获设备。

struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
struct v4l2_format format;
double hscale, vscale;
double aspect;
int dwidth, dheight;

memset (&cropcap, 0, sizeof (cropcap));
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

if (-1 == ioctl (fd, VIDIOC_CROPCAP, &cropcap)) {
    perror ("VIDIOC_CROPCAP");
    exit (EXIT_FAILURE);
}

memset (&crop, 0, sizeof (crop));
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

if (-1 == ioctl (fd, VIDIOC_G_CROP, &crop)) {
    if (errno != EINVAL) {
        perror ("VIDIOC_G_CROP");
        exit (EXIT_FAILURE);
    }

    /* Cropping not supported. */
    crop.c = cropcap.defrect;
}

memset (&format, 0, sizeof (format));
format.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

if (-1 == ioctl (fd, VIDIOC_G_FMT, &format)) {
    perror ("VIDIOC_G_FMT");
    exit (EXIT_FAILURE);
}

/* The scaling applied by the driver. */

hscale = format.fmt.pix.width / (double) crop.c.width;
vscale = format.fmt.pix.height / (double) crop.c.height;

aspect = cropcap.pixelaspect.numerator /
     (double) cropcap.pixelaspect.denominator;
aspect = aspect * hscale / vscale;

/* Devices following ITU-R BT.601 do not capture
   square pixels. For playback on a computer monitor
   we should scale the images to this size. */

dwidth = format.fmt.pix.width / aspect;
dheight = format.fmt.pix.height;