1.28. 影像裁剪、插入和縮放——CROP API

注意

CROP API 大部分已被新的 SELECTION API 取代。除畫素寬高比檢測(由 VIDIOC_CROPCAP 實現,且在 SELECTION API 中沒有等效項)外,新 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

影像裁剪、插入和縮放

裁剪、插入和縮放過程

對於捕獲裝置,可以取樣的區域的左上角座標、寬度和高度由 VIDIOC_CROPCAP ioctl 返回的 v4l2_cropcap 結構體的 bounds 子結構給出。為了支援廣泛的硬體,本規範沒有定義原點或單位。然而,根據慣例,驅動程式應水平地將未縮放的樣本相對於 0H(水平同步脈衝的前沿,參見 圖 4.1. 行同步)進行計數。垂直方向上,ITU-R 第一場的行號(參見 ITU R-525 行號,針對 525 行625 行),如果驅動程式可以捕獲兩個場,則乘以二。

源矩形(即實際取樣的區域)的左上角座標、寬度和高度由 v4l2_crop 結構體使用與 v4l2_cropcap 結構體相同的座標系給出。應用程式可以使用 VIDIOC_G_CROPVIDIOC_S_CROP ioctl 來獲取和設定此矩形。它必須完全位於捕獲邊界內,驅動程式可以根據硬體限制進一步調整請求的大小和/或位置。

每個捕獲裝置都有一個預設的源矩形,由 v4l2_cropcap 結構體的 defrect 子結構給出。此矩形的中心應與影片訊號活動影像區域的中心對齊,並覆蓋驅動程式編寫者認為的完整影像。驅動程式應在首次載入時將源矩形重置為預設值,但之後不再重置。

對於輸出裝置,這些結構體和 ioctl 相應地使用,定義了影像將被插入影片訊號的目標矩形。

1.28.2. 縮放調整

影片硬體可能存在各種裁剪、插入和縮放限制。它可能只支援放大或縮小,只支援離散的縮放因子,或者在水平和垂直方向具有不同的縮放能力。此外,它可能根本不支援縮放。同時,v4l2_crop 矩形可能需要對齊,並且源和目標矩形可能具有任意的上下尺寸限制。特別是,v4l2_crop 結構體中的最大 widthheight 可能小於 v4l2_cropcap 結構體的 bounds 區域。因此,像往常一樣,驅動程式應調整請求的引數並返回實際選擇的值。

應用程式可以首先更改源矩形或目標矩形,因為它們可能更喜歡特定的影像尺寸或影片訊號中的某個區域。如果驅動程式必須同時調整兩者以滿足硬體限制,則最後請求的矩形應具有優先權,驅動程式應優先調整另一個矩形。然而,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;