Android: Create Allocation from pixels array for renderscript - android

I am trying to use the Android Renderscript for blurring an image. My input is an array of integers that containt the pixel's colors. Here's what I did and not worked. The application shut down without any error message on Galaxy S device
bmp.getPixels(pixels, 0, bmp.getWidth(), 0, 0, bmp.getWidth(), bmp.getHeight());
Allocation input = Allocation.createSized(rs, Element.I32(rs), pixels.length);
input.copy1DRangeFrom(0, pixels.length, pixels);
Allocation output = Allocation.createTyped(rs, input.getType());
ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
script.setRadius(6f);
script.setInput(input);
script.forEach(output);
output.copyTo(pixels);

You'll need to look at the logcat output (make sure no filters are on in Android Studio / Eclipse), it will show you the crash.
The problem you're seeing is most likely because your input Allocation element type doesn't match the output. They need to be the same. Rather than call Allocation.createSized() and specify an element, just call Allocation.createFromBitmap() and provide it with your input Bitmap object. Then copy the input Bitmap into the Allocation.

Related

RenderScript copy 3D allocation in rs file

RenderScript allocations can be 1D, 2D or 3D. Almost all methods support these 3, but the copy method is missing 3D. According to Android developers, below are all data access functions:
rsAllocationCopy1DRange Copy consecutive cells between allocations
rsAllocationCopy2DRange Copy a rectangular region of cells between allocations
rsAllocationVLoadX Get a vector from an allocation of scalars
rsAllocationVStoreX Store a vector into an allocation of scalars
rsGetElementAt Return a cell from an allocation
rsGetElementAtYuv_uchar_U Get the U component of an allocation of YUVs
rsGetElementAtYuv_uchar_V Get the V component of an allocation of YUVs
rsGetElementAtYuv_uchar_Y Get the Y component of an allocation of YUVs
rsSample Sample a value from a texture allocation
rsSetElementAt Set a cell of an allocation
As you can see, there is no rsAllocationCopy3DRange. I tried it anyway with this code:
rsAllocationCopy3DRange(output, 0, 0, 0, 0, 0, w, h, d, data, 0, 0, 0, 0, 0);
And the compile failed.
How to copy 3D allocations? I can write a kernel function just return the input and then rsForEach that function, but seems very inefficient for a simple copy.
Why is there no 3D copy? It doesn't make sense. Why is that Java/Kotlin side has 3D copy, but rs side doesn't?

Renderscript allocation from bitmap

I'm trying to get into render script and was confused regarding allocations usage. Almost all examples show next algorithm:
Create In and Out Bitmap
Create In and Out Allocation from Bitmaps In and Out correspondingly
Configure script and perform forEach method
copy result from Out allocation into bitmap using copyTo method
Something like that:
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lena);
Bitmap dstBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
Allocation allocationIn = Allocation.createFromBitmap(renderScript, srcBitmap);
Allocation allocationOut = Allocation.createFromBitmap(renderScript, dstBitmap);
scriptColorMatrix.setGreyscale();
scriptColorMatrix.forEach(allocationIn, allocationOut);
//no difference after removing this line
allocationOut.copyTo(dstBitmap);
imagePreview.setImageBitmap(dstBitmap);
This works, but it also works even if I omit step 4 by removing:
allocationOut.copyTo(dstBitmap);
Lets go further and lower brightness after grayscale:
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lena);
Bitmap dstBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
Allocation allocationIn = Allocation.createFromBitmap(renderScript, srcBitmap);
Allocation allocationOut = Allocation.createFromBitmap(renderScript, dstBitmap);
scriptColorMatrix.setGreyscale();
scriptColorMatrix.forEach(allocationIn, allocationOut);
//reset color matrix
scriptColorMatrix.setColorMatrix(new Matrix4f());
//adjust brightness
scriptColorMatrix.setAdd(-0.5f, -0.5f, -0.5f, 0f);
//Performing forEach vise versa (from out to in)
scriptColorMatrix.forEach(allocationOut, allocationIn);
imagePreview.setImageBitmap(srcBitmap);
Shortly describing the code above, we performed grayscale collor matrix from In allocation into Out one, and brightness adjustment in backward direction. I never called copyTo method, but at the end I've got result in srcBitmap and it was correct.
That's not the end. Lets go deeper. I'll leave only one bitmap and one Allocation:
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lena);
Allocation allocationIn = Allocation.createFromBitmap(renderScript, srcBitmap);
scriptColorMatrix.setGreyscale();
scriptColorMatrix.forEach(allocationIn, allocationIn);
//reset color matrix
scriptColorMatrix.setColorMatrix(new Matrix4f());
//adjust brightness
scriptColorMatrix.setAdd(-0.5f, -0.5f, -0.5f, 0f);
scriptColorMatrix.forEach(allocationIn, allocationIn);
imagePreview.setImageBitmap(srcBitmap);
The result is the same...
Can anybody explain why does it happen and where to use copyTo and where I can use targeting Bitmap without it?
The Allocation objects are needed in order to provide the proper mapping of Bitmap to something Renderscript understands. If you are targeting API 18 or higher, the Allocation.createFromBitmap() methods you are using are automatically giving in the flag USAGE_SHARED, which tries to have the Allocation use the same backing memory as the Bitmap object. So the two are linked but technically the copyTo() method is still needed as the RS implementation may need to synchronize it back. On some platforms this may have already happened where others may cause this to pause as DMA or other mechanisms are updating the Bitmap's backing memory with whatever changes were made by the RS code.
As for why you can reverse the in/out Allocation order when calling scripts - it's valid and up to you to get the arguments and order correct. To RS they are just objects which point to some type of backing data to be manipulated. Since both were created with the Allocation.createFromBitmap() call, they can be used as either input or output as long as the Bitmap objects are mutable.
Similarly, using the same Allocation for the input and output is not normal, but also not invalid. It just means your input is changing on the fly. As long as your script is not accessing other Elements in the data when the root function is called for a specific Element, then it should work (as you are seeing.)

Android Blur with RenderScript

I try to make a gaussian blur on an android Bitmap but I get this error:
rsAssert failed: !mTypes.size() and
rsAssert failed: !mElements.size()
Here is my code :
public Bitmap blurBitmap(Bitmap src) {
Bitmap outBitmap = src.copy(src.getConfig(), true);
final RenderScript rs = RenderScript.create(this);
final Allocation input = Allocation.createFromBitmap(rs, src);
final Allocation output = Allocation.createFromBitmap(rs, outBitmap);
final ScriptIntrinsicBlur script =
ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
script.setRadius(25f);
script.setInput(input);
script.forEach(output);
output.copyTo(outBitmap);
rs.destroy();
return outBitmap;
}
Note that I used android.support.v8.renderscript to ensure compatibility with android lower versions.
Someone would have an idea to fix it?
Thanks.
Martin
The Element arguments of ScriptIntrinsicBlur should be the same of Allocation's element, so you should use Allocation.getElement(), rather than direct Element.U8_4(rs).
final ScriptIntrinsicBlur script =
ScriptIntrinsicBlur.create(rs, input.getElement());
And you might also want move those final to class private member, since some of them might be different every time your bitmap is different.
And FYI, script.setRadius(25f) is way too high, that will cause slow calculation. If you need such heavy blur, you might consider to scale down the original bitmap at certain level, and blur it, then scale up to your canvas, which will be much faster than heavy blur for a huge image.
One more thing, if you don't care to keep the original bitmap, the allocation of input and output can be the same one, which might save some memory.
While there isn't a good way to increase blur radius, for very large images you can essentially fake it by scaling down (ex .5 original size), blurring, then scaling up to original size. Be careful not to scale too far down as the resulting bitmap will become pixelated.

RenderScript Intrinsics Gaussian blur

How do I properly use the RenderScript Intrinsics.
as shown http://android-developers.blogspot.com/2013/08/renderscript-intrinsics.html
//Assuming my original Bitmap is "bm"
Bitmap outputBitmap = Bitmap.createBitmap(bm.getHeight(),
bm.getWidth(), Config.ARGB_8888);
RenderScript rs = RenderScript.create(getApplicationContext());
ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur
.create(rs, Element.U8_4(rs));
Allocation tmpIn = Allocation.createFromBitmap(rs, bm);
Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
theIntrinsic.setRadius(25.f);
theIntrinsic.setInput(tmpIn);
theIntrinsic.forEach(tmpOut);
tmpOut.copyTo(outputBitmap);
bm.recycle();
rs.destroy
I get some black areas of the outputBitmap on initial layout, but which get filled in after the user scrolls the Scroller of the HorizontalScrollView, making the drawable "refresh" itself.
I get this error too (if it helps):
09-01 05:54:11.246: E/RenderScript(11423): rsAssert failed: !mElements.size(), in frameworks/rs/rsElement.cpp at 375
any suggestions regarding proper use of the RS will help.
I think the problem is that you switched the order of the height and width arguments. It should be:
Bitmap outputBitmap = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Config.ARGB_8888);
I'm guessing you've got some issue with the UI parts rather than the RS parts. The RS parts look fine; maybe try a outputBitmap.prepareToDraw() after the RS bits have finished?
Note that in general it's not a great idea to create and destroy RS contexts in the critical path like that. There's potentially a nontrivial startup/teardown cost depending on the hardware resources that have to be allocated, so it would be much better to allocate it at startup and use it for the lifetime of the application.

OutOfMemoryError while create a bitmap

I want to send a fax from my app.
A fax document has a resolution of 1728 x 2444 pixels.
So I create a bitmap, add text and/or pictures and encode it to CCITT (Huffman):
Bitmap image = Bitmap.createBitmap(1728, 2444, Config.ALPHA_8);
Canvas canvas = new Canvas(image);
canvas.drawText("This is a fax", 100, 100, new Paint());
ByteBuffer buffer = ByteBuffer.allocateDirect(image.getWidth() * image.getHeight());
image.copyPixelsToBuffer(buffer);
image.recycle();
encodeCCITT(buffer, width, height);
This works perfect on my Galaxy SII (64 MB heap size), but not at emulator (24 MB). After creating the second fax page I get "4223232-byte external allocation too large for this process...java.lang.OutOfMemoryError" while allocating the buffer.
I already reduced color depth from ARGB_8888 (4 byte per pixel) to ALPHA_8 (1 byte), because fax pages are monochrome anyway.
I need this resolution and I need to have access to the pixels for encoding.
What is the best way?
Android doesn't support 1-Bpp bitmaps, and the Java heap size limit of 24/32/48MB is part of Android. Real devices can't allocate more than the Java heap limit no matter how much RAM they have. There appear to be only two possible solutions:
1) Work within the limitations of the Java heap.
2) Use native code (NDK).
In native code you can allocate the entire available RAM of the device. The only down side is that you will need to write your own code to edit and encode your bitmap.
In addition to BitBank's already good answer, you have to null the reference if you want the Garbage collector to actually clean up your Bitmap's references. The documentation for that method states:
This is an advanced call, and normally need not be called, since the
normal GC process will free up this memory when there are no more
references to this bitmap.
instead of copy all pixels to a ByteBuffer, you can copy step by step. Here with a int[] array. So, you need less memory:
int countLines = 100;
int[] pixels = new int[width * countLines];
for (int y = 0; y < heigth; y += countLines) {
image.getPixels(line, 0, width, 0, y, width, countLines);
// do something with pixels...
image.setPixels(line, 0, width, 0, y, width, countLines);
}

Categories

Resources