使用 Renderscript 启动选项裁剪图像数据

Posted

技术标签:

【中文标题】使用 Renderscript 启动选项裁剪图像数据【英文标题】:Crop image data using Renderscript Launch Options 【发布时间】:2016-03-04 09:04:07 【问题描述】:

我有一个灰度图像 dataIn 的字节 [] 数组,尺寸为宽度 * 高度。现在,我想通过应用平移 (dx, dy) 并切断边界外区域来裁剪图像,以便 dataOut 具有尺寸 (width-abs(dx))*(height-abs(dy ))。

在 RenderScript 中,我会为输入和输出使用 2-d uchar-Allocation。为了有效地应用裁剪操作,它正在考虑将 LaunchOptions 与(例如) setX(dx,width) 和 setY(0, height-dy) 一起使用,并应用一个简单的内核,它只从原始尺寸。

但是,当使用启动选项时,out-Allocation 仍然具有原始尺寸宽度 * 高度,即裁剪部分将仅显示为零 - 但是,我实际上希望它们被删除,即 out-Allocation 是降维。

问题:RS 中是否有解决方案可以更优雅地执行此裁剪工作?感谢您的反馈。

更新:我想,我找到了解决方案。它是通过从一开始就将 out-Allocation 定义为缩减维度的脚本全局,传递 dx 和 dy 以及全局变量,然后应用 rsSetElementAt_uchar 来设置 out-Allocation 的值。稍后会提供更新。

【问题讨论】:

【参考方案1】:

所以,这是我的快速 rs-crop 工具,裁剪 500k 像素的图像需要 5 毫秒。它使用 LaunchOptions 和减少的尺寸来裁剪输出。如果您需要裁剪位图,只需分别使用元素类型 U8_4 和分配 uchar_4 而不是 U8 和 uchar。

crop.rs 文件:

#pragma version(1)
#pragma rs java_package_name(com.xxx.yyy)
#pragma rs_fp_relaxed

int32_t width;
int32_t height;

rs_allocation croppedImg;
uint xStart, yStart;

void __attribute__((kernel)) doCrop(uchar in,uint32_t x, uint32_t y) 
    rsSetElementAt_uchar(croppedImg,in, x-xStart, y-yStart);

Java 部分:

// data1 is the byte[] array with (grayvalue) data of size 
// width*height you want to crop.

// define crop shift (dx, dy) here
int dx=0;       // (-width < dx < width);
int dy=250;     // (- height < dy < height);

int xStart=0, xEnd=0;
int yStart=0, yEnd=0;

// x direction
if (dx<0) 
    xStart= Math.abs(dx);
    xEnd=width;

else 
    xStart = 0;
    xEnd = width - Math.abs(dx);


// same for y direction
if (dy<0) 
    yStart= Math.abs(dy);
    yEnd=height;

else 
    yStart = 0;
    yEnd = height - Math.abs(dy);


// initiate rs and crop script
RenderScript rs = RenderScript.create(this);
ScriptC_crop mcropScr=new ScriptC_crop (rs);

// define allocations. Note the reduced size of cropAlloc 
Type.Builder typeUCHAR = new Type.Builder(rs, Element.U8(rs));  
typeUCHAR.setX(width).setY(height);

inAlloc = Allocation.createTyped(rs, typeUCHAR.create());
inAlloc.copyFrom(data1);

Type.Builder TypeUCHARCropped = new Type.Builder(rs, Element.U8(rs));
TypeUCHARCropped.setX(xEnd-xStart).setY(yEnd-yStart);

Allocation cropAlloc = Allocation.createTyped(rs,  TypeUCHARCropped.create());
mcropScr.set_croppedImg(cropAlloc);
mcropScr.set_xStart(xStart);
mcropScr.set_yStart(yStart);

Script.LaunchOptions lo = new Script.LaunchOptions();
lo.setX(xStart, xEnd);
lo.setY(yStart, yEnd);

mcropScr.forEach_doCrop(inAlloc, lo);


byte[] data1_cropped =new byte[(xEnd-xStart)*(yEnd-yStart)];
cropAlloc.copyTo(data1_cropped);

【讨论】:

不适用于 U8_4 位图android.support.v8.renderscript.RSIllegalArgumentException: Allocation kind is USER, type UNSIGNED_8 of 4 bytes, passed bitmap was ARGB_8888 我更改为 ARGB_8888,返回的位图变为 null 任何建议。 Java方法结束:byte[] data1_cropped =new byte[targetWidth*targetHeight*4]; cropAlloc.copyTo(data1_cropped); Bitmap out = BitmapFactory.decodeByteArray(data1_cropped,0, data1_cropped.length); 修复了替换位图代码,这有效:Bitmap out = Bitmap.createBitmap(targetWidth, targetHeight, in.getConfig()); cropAlloc.copyTo(out);退出;【参考方案2】:

与另一个答案类似的想法,但这符合 Google 的 API for Intrinsics 的风格:

#pragma version(1)
#pragma rs java_package_name(com.sicariusnoctis.collaborativeintelligence)
#pragma rs_fp_relaxed

rs_allocation input;
uint32_t xStart, yStart;

uchar4 RS_KERNEL crop(uint32_t x, uint32_t y) 
    return rsGetElementAt_uchar4(input, x + xStart, y + yStart);

设置:

fun setup(
    width: Int, height: Int,
    new_width: Int, new_height: Int,
    xStart: Int, yStart: Int
) 
    val inputType = Type.createXY(rs, Element.RGBA_8888(rs), width, height)
    val outputType = Type.createXY(rs, Element.RGBA_8888(rs), new_width, new_height)

    inputAllocation = Allocation.createTyped(rs, inputType, Allocation.USAGE_SCRIPT)
    outputAllocation = Allocation.createTyped(rs, outputType, Allocation.USAGE_SCRIPT)

    crop = ScriptC_crop(rs)
    crop._xStart = xStart.toLong()
    crop._yStart = yStart.toLong()
    crop._input = inputAllocation

并执行:

fun execute(inputArray: ByteArray): ByteArray 
    inputAllocation.copyFrom(inputArray)
    crop.forEach_crop(outputAllocation)

    val outputArray = ByteArray(outputAllocation.bytesSize)
    outputAllocation.copyTo(outputArray)
    return outputArray

【讨论】:

以上是关于使用 Renderscript 启动选项裁剪图像数据的主要内容,如果未能解决你的问题,请参考以下文章

iOS和Windows Phone的Android Renderscript等效于什么?

在 iOS/swift 中上传图像时如何删除裁剪选项

如何从 recyclerview 裁剪图像并使用新裁剪的图像更新 recyclerview?

你如何启动cropper以在fengyuanchencropper中裁剪图像

裁剪图像,右下角有重要部分

Renderscript 致命信号 11 (SIGSEGV) 代码 1 (SEGV_MAPERR) 故障地址