簡體   English   中英

如何在 Android RenderScript 中一次縮放、裁剪和旋轉

[英]How to scale, crop, and rotate all at once in Android RenderScript

是否可以使用 Y'UV 格式並使用 RenderScript 拍攝相機圖像:

  1. 將其轉換為 RGBA
  2. 將其裁剪到某個區域
  3. 必要時旋轉它

是的! 我想出了如何並認為我會與他人分享。 RenderScript 有一點學習曲線,更簡單的例子似乎有幫助。

裁剪時,您仍然需要設置輸入和輸出分配以及腳本本身的分配。 乍一看似乎很奇怪,但輸入和輸出分配必須具有相同的大小,因此如果您正在裁剪,則需要設置另一個分配來寫入裁剪后的輸出。 稍后會詳細介紹。

#pragma version(1)
#pragma rs java_package_name(com.autofrog.chrispvision)
#pragma rs_fp_relaxed

/*
 * This is mInputAllocation
 */
rs_allocation gInputFrame;

/*
 * This is where we write our cropped image
 */
rs_allocation gOutputFrame;

/*
 * These dimensions define the crop region that we want
 */
uint32_t xStart, yStart;
uint32_t outputWidth, outputHeight;

uchar4 __attribute__((kernel)) yuv2rgbFrames(uchar4 in, uint32_t x, uint32_t y)
{
    uchar Y = rsGetElementAtYuv_uchar_Y(gInputFrame, x, y);
    uchar U = rsGetElementAtYuv_uchar_U(gInputFrame, x, y);
    uchar V = rsGetElementAtYuv_uchar_V(gInputFrame, x, y);

    uchar4 rgba = rsYuvToRGBA_uchar4(Y, U, V);

    /* force the alpha channel to opaque - the conversion doesn't seem to do this */
    rgba.a = 0xFF;

    uint32_t translated_x = x - xStart;
    uint32_t translated_y = y - yStart;

    uint32_t x_rotated = outputWidth - translated_y;
    uint32_t y_rotated = translated_x;

    rsSetElementAt_uchar4(gOutputFrame, rgba, x_rotated, y_rotated);
    return rgba;
}

要設置分配:

private fun createAllocations(rs: RenderScript) {

    /*
     * The yuvTypeBuilder is for the input from the camera.  It has to be the
     * same size as the camera (preview) image
     */
    val yuvTypeBuilder = Type.Builder(rs, Element.YUV(rs))
    yuvTypeBuilder.setX(mImageSize.width)
    yuvTypeBuilder.setY(mImageSize.height)
    yuvTypeBuilder.setYuvFormat(ImageFormat.YUV_420_888)
    mInputAllocation = Allocation.createTyped(
        rs, yuvTypeBuilder.create(),
        Allocation.USAGE_IO_INPUT or Allocation.USAGE_SCRIPT)

    /*
     * The RGB type is also the same size as the input image.  Other examples write this as
     * an int but I don't see a reason why you wouldn't be more explicit about it to make
     * the code more readable.
     */
    val rgbType = Type.createXY(rs, Element.RGBA_8888(rs), mImageSize.width, mImageSize.height)

    mScriptAllocation = Allocation.createTyped(
        rs, rgbType,
        Allocation.USAGE_SCRIPT)

    mOutputAllocation = Allocation.createTyped(
        rs, rgbType,
        Allocation.USAGE_IO_OUTPUT or Allocation.USAGE_SCRIPT)

    /*
     * Finally, set up an allocation to which we will write our cropped image.  The
     * dimensions of this one are (wantx,wanty)
     */
    val rgbCroppedType = Type.createXY(rs, Element.RGBA_8888(rs), wantx, wanty)
    mOutputAllocationRGB = Allocation.createTyped(
        rs, rgbCroppedType,
        Allocation.USAGE_SCRIPT)
}

最后,由於您正在裁剪,您需要告訴腳本在調用之前要做什么。 如果圖像大小沒有改變,您可能可以通過移動 LaunchOptions 和變量設置來優化它,以便它們只出現一次(而不是每次),但我將它們留在這里作為示例以使其更清晰。

override fun onBufferAvailable(a: Allocation) {
    // Get the new frame into the input allocation
    mInputAllocation!!.ioReceive()

    // Run processing pass if we should send a frame
    val current = System.currentTimeMillis()
    if (current - mLastProcessed >= mFrameEveryMs) {
        val lo = Script.LaunchOptions()

        /*
         * These coordinates are the portion of the original image that we want to
         * include.  Because we're rotating (in this case) x and y are reversed
         * (but still offset from the actual center of each dimension)
         */

        lo.setX(starty, endy)
        lo.setY(startx, endx)

        mScriptHandle.set_xStart(lo.xStart.toLong())
        mScriptHandle.set_yStart(lo.yStart.toLong())

        mScriptHandle.set_outputWidth(wantx.toLong())
        mScriptHandle.set_outputHeight(wanty.toLong())

        mScriptHandle.forEach_yuv2rgbFrames(mScriptAllocation, mOutputAllocation, lo)

        val output = Bitmap.createBitmap(
            wantx, wanty,
            Bitmap.Config.ARGB_8888
        )

        mOutputAllocationRGB!!.copyTo(output)

        /* Do something with the resulting bitmap */
        listener?.invoke(output)

        mLastProcessed = current
    }
}

所有這些可能看起來有點多,但它非常快 - 比在 java/kotlin 端進行旋轉要快得多,而且由於 RenderScript 能夠在圖像的一個子集上運行內核函數,它的開銷比創建位圖少創建第二個,裁剪的。

對我來說,所有的旋轉都是必要的,因為 RenderScript 看到的圖像是從相機旋轉 90 度的。 有人告訴我,這是擁有三星手機的某種特殊性。

RenderScript 起初令人生畏,但是一旦您習慣了它的功能,它就不會那么糟糕。 我希望這對某人有幫助。

感謝這篇文章。 我是renderscript的新手,對YUV和RGB格式的工作原理幾乎沒有了解。 我正在使用您的代碼將YUV轉換為RGB,旋轉但不進行裁剪(我將0用作start_x和start_y以及實際寬度和高度,假設這會給我整個圖像)。 但是,我的應用程序崩潰時沒有堆棧並且出現錯誤:

A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xb8 in tid 29575

我想知道我在想什么。 我對您發布的代碼也不太困惑,因為我看不到將mInputAllocation中的接收緩沖區傳遞到renderscript的任何地方。 那么renderscript如何獲取輸入數據? 以及如何將其放回mOutputAllocation和mOutputAllocationRGB?

您能否對此進行詳細說明或為我指明正確的方向?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM