I am trying to chain two Renderscripts : ScriptIntrinsicBlur and ScriptIntrinsicColorMatrix.
I want to blur and image and then apply a color filter to it.
Here is my current code (I tried many different implementations, including ScriptGroups, that I can't get to work :
final ScriptIntrinsicBlur scriptBlur = ScriptIntrinsicBlur.create(
mRenderScript,
Element.U8_4(mRenderScript));
final Allocation input = Allocation.createFromBitmap(mRenderScript,
bmp);
Bitmap blurOutBitmap = bmp.copy(bmp.getConfig(), true);
final Allocation output = Allocation.createFromBitmap(
mRenderScript, blurOutBitmap);
scriptBlur.setRadius(mBlur_Radius);
scriptBlur.setInput(input);
scriptBlur.forEach(output);
bmp.recycle();
output.copyTo(blurOutBitmap);
mRenderScript.finish();
final ScriptIntrinsicColorMatrix scriptColor = ScriptIntrinsicColorMatrix
.create(mRenderScript, Element.U8_4(mRenderScript));
/** for a first test, I am using a simple blue filter **/
Matrix3f mat = new Matrix3f(new float[] {
1, 0, 1,
0, 1, 1,
0, 0, 1
});
scriptColor.setColorMatrix(mat);
final Allocation colorInput = Allocation.createFromBitmap(mRenderScript,
blurOutBitmap);
Bitmap outBitmap = bmp.copy(blurOutBitmap.getConfig(), true);
final Allocation colorOutput = Allocation.createFromBitmap(
mRenderScript, outBitmap);
scriptColor.forEach(colorInput, colorOutput);
blurOutBitmap.recycle();
colorOutput.copyTo(outBitmap);
displayBitmap(outBitmap);
This code produces very ugly artifacts on the image (parallel red lines) and if I try to use a ScriptGroup instead it simply crashes.
Does anybody with Renderscript Experience can help me decipher why ? Since there are very few samples or documentation on that topic, I am stuck trying random modifications.
Here is the correct way to do this (or at least one that works, this case is a bit buggy) :
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lenna);
ScriptIntrinsicBlur scriptBlur = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript));
scriptBlur.setRadius(5f);
ScriptIntrinsicColorMatrix scriptColor = ScriptIntrinsicColorMatrix.create(mRenderScript, Element.U8_4(mRenderScript));
final Allocation input = Allocation.createFromBitmap(mRenderScript, bitmap,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT | Allocation.USAGE_SHARED);
scriptBlur.setInput(input);
Bitmap outBitmap = bitmap.copy(bitmap.getConfig(), true);
final Allocation output = Allocation.createTyped(mRenderScript, input.getType());
scriptColor.setColorMatrix(new Matrix4f(
new float[]{1, 0f, 0f, 0,
1, 1, 0f, 0,
1, 0f, 1, 0,
0, 0, 0, 1}
));
ScriptGroup.Builder b = new ScriptGroup.Builder(mRenderScript);
b.addKernel(scriptBlur.getKernelID());
b.addKernel(scriptColor.getKernelID());
b.addConnection(input.getType(), scriptBlur.getKernelID(), scriptColor.getKernelID());
ScriptGroup group = b.create();
// group.setInput(scriptBlur.getKernelID(),input);
group.setOutput(scriptColor.getKernelID(), output);
group.execute();
output.copyTo(outBitmap);
return outBitmap;
Related
I have a class that uses a color matrix to add image effects on an ImageView. I have several color matrices for contrast,exposure,temperature and saturation.
Ex:
public static Bitmap changeContrast(Bitmap bmp, float contrast)
{
ColorMatrix cm = new ColorMatrix(new float[]
{
contrast, 0, 0, 0, 0,
0, contrast, 0, 0, 0,
0, 0, contrast, 0, 0,
0, 0, 0, 1, 0
});
return getBitmapFromColorMatrix(cm, bmp);
}
Now my problem is, the sharpening of the image. There seems to be no color matrix for these. Please help.
I tried sharpening the image using the code from here but it takes too long to process and I do not know what valid values should be passed to the weight parameter of the sharpenImage(Bitmap src, double weight) method.
It may be too late but i'd just wanted to share a fast way to sharpen an image.
public static Bitmap doSharpen(Bitmap original, float[] radius) {
Bitmap bitmap = Bitmap.createBitmap(
original.getWidth(), original.getHeight(),
Bitmap.Config.ARGB_8888);
RenderScript rs = RenderScript.create(yourContext);
Allocation allocIn = Allocation.createFromBitmap(rs, original);
Allocation allocOut = Allocation.createFromBitmap(rs, bitmap);
ScriptIntrinsicConvolve3x3 convolution
= ScriptIntrinsicConvolve3x3.create(rs, Element.U8_4(rs));
convolution.setInput(allocIn);
convolution.setCoefficients(radius);
convolution.forEach(allocOut);
allocOut.copyTo(bitmap);
rs.destroy();
return bitmap;
}
And here are 3 different sharpen types that i created:
// low
private static void loadBitmapSharp() {
float[] sharp = { -0.60f, -0.60f, -0.60f, -0.60f, 5.81f, -0.60f,
-0.60f, -0.60f, -0.60f };
//you call the method above and just paste the bitmap you want to apply it and the float of above
yourbitmap = doSharpen(getbitmap, sharp));
}
// medium
private static void loadBitmapSharp1() {
float[] sharp = { 0.0f, -1.0f, 0.0f, -1.0f, 5.0f, -1.0f, 0.0f, -1.0f,
0.0f
};
//you call the method above and just paste the bitmap you want to apply it and the float of above
yourbitmap = doSharpen(getbitmap, sharp));
}
// high
private static void loadBitmapSharp2() {
float[] sharp = { -0.15f, -0.15f, -0.15f, -0.15f, 2.2f, -0.15f, -0.15f,
-0.15f, -0.15f
};
//you call the method above and just paste the bitmap you want to apply it and the float of above
yourbitmap = doSharpen(getbitmap, sharp));
}
You can also apply them direct to a bitmap without a void, its fast simple and there are good results!
Here are the values that i used for color temperature with a seekbar. (you asked them into a comment of one question that i asked)
public static Bitmap doTemperature(Bitmap bmp, float temp){
ColorMatrix cm = new ColorMatrix(new float[]
{
1, 0, 0, temp, 0,
0, 1, 0, temp/2, 0,
0, 0, 1, temp/4, 0,
0, 0, 0, 1, 0
});
Bitmap ret = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());
Canvas canvas = new Canvas(ret);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(cm));
canvas.drawBitmap(bmp, 0, 0, paint);
return ret;
}
//you call it with this method
private void loadBitmapTemp() {
//private Seekbar temp_value;
temp_value = (SeekBar) findViewById(R.id.temp_value);
temp_value.setMax(100);
temp_value.setProgress(50);
int progressTemp = temp_value.getProgress();
progressTemp -= 50;
float temp = (float) progressTemp / 220;
progress_text.setVisibility(View.VISIBLE);
progress_text.setText(" " + progressTemp * 2);
yourBitmap = doTemperature(getbitmap, temp));
}
Improving the first answer here, adding a seekbar (slider) to change the sharpness of an image.
public Bitmap doSharpen(Bitmap original, float multiplier) {
float[] sharp = { 0, -multiplier, 0, -multiplier, 5f*multiplier, -multiplier, 0, -multiplier, 0};
Bitmap bitmap = Bitmap.createBitmap(
original.getWidth(), original.getHeight(),
Bitmap.Config.ARGB_8888);
RenderScript rs = RenderScript.create(MainActivity.this);
Allocation allocIn = Allocation.createFromBitmap(rs, original);
Allocation allocOut = Allocation.createFromBitmap(rs, bitmap);
ScriptIntrinsicConvolve3x3 convolution
= ScriptIntrinsicConvolve3x3.create(rs, Element.U8_4(rs));
convolution.setInput(allocIn);
convolution.setCoefficients(sharp);
convolution.forEach(allocOut);
allocOut.copyTo(bitmap);
rs.destroy();
return bitmap;
}
I had to modify it a little according to my project. Instead of taking the convolution matrix in method, i declared it inside the method. We are going to take the changed variable of seekbar instead.
sharpnessSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
Bitmap sharpenedBitmap = doSharpen(btmp,(float)i);
imageV.setImageBitmap(sharpenedBitmap);
}
Inside the setOnSeekBarChangeListener we get the changed value which is int i and pass it with doSharpen method so that it can be used in the convolution matrix which is defined inside doSharpen() Method.
If I load a bitmap with ALPHA_8 it will create an bitmap with the a greyscale and transparancy. This is exactly what I want.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ALPHA_8;
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test_bitmap, options);
I want to merge two bitmaps. To do so I create a canvas with ALPHA_8 and draw the same bitmap on the canvas.
But the result is different. It will keep the alpha, but everything what was grey will be black. So I see only the contours and no greyscale.
resultBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ALPHA_8);
resultCanvas = new Canvas(resultBitmap);
resultCanvas.drawBitmap(bitmap, 0, 0, null);
I found 2 workarounds.
A) Use ARGB_8888 instead of ALPHA_8, but this will result in a far larger image.
resultBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
B) Use a color matrix to convert the image to alpha channel, but this will result in the grey to be semi transparant, instead of a grey scale.
float[] matrix = new float[] {
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
-1f, 0, 0, 1f, 0};
Paint grayToAlpha = new Paint();
grayToAlpha.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(matrix)));
resultCanvas.drawBitmap(bitmap, 0, 0, grayToAlpha);
Both workarounds are not perfect. So my question is:
Is there a way to get the same result with the canvas as with the bitmap?
Or
Is there a better way of merging two bitmaps with ALPHA_8 together?
I got a problem with an Air Native Extension in Android.
The ANE receive a Bitmap from the Actionscript side, compress it in jpeg format and send it back to the Actionscript that will write it on the storage.
Everything is good but one last thing.
It seems that the channels order for Actionscript is different than in Android, so my compressed image have RED colors in place of BLUE.. here is the code:
Actionscript (I'm using a library called deng.fzip.FZipLibrary to get the bitmap from a zip package)
__image = __fl.getBitmapData(path);
__de = new DataExchange();
__ba = __de.bitmapEncode(__image) as ByteArray;
Android
...
try {
inputValue = (FREBitmapData)arg1[0];
inputValue.acquire();
int srcWidth = inputValue.getWidth();
int srcHeight = inputValue.getHeight();
Bitmap bm = Bitmap.createBitmap(srcWidth, srcHeight, Config.ARGB_8888);
bm.copyPixelsFromBuffer(inputValue.getBits());
ByteArrayOutputStream os = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 80, os);
compressed = os.toByteArray();
inputValue.release();
} catch (Exception e) {
e.printStackTrace();
}
try {
returnValue = FREByteArray.newByteArray();
returnValue.setProperty("length", FREObject.newObject(compressed.length));
returnValue.acquire();
ByteBuffer returnBytes = returnValue.getBytes();
returnBytes.put(compressed, 0, compressed.length);
returnValue.release();
}
...
Anyone have an idea on how to convert the red to blue in the android side before send the image back? Or it needs to be done in the actionscript side?
Thank you very much and regards,
Gianpiero
You can switch the colors like this:
(This code does not origin from me, unfortunately I forgot where I found it, so credit goes somewhere else)
private Bitmap m_encodingBitmap = null;
private Canvas m_canvas = null;
private Paint m_paint = null;
private final float[] m_bgrToRgbColorTransform =
{
0, 0, 1f, 0, 0,
0, 1f, 0, 0, 0,
1f, 0, 0, 0, 0,
0, 0, 0, 1f, 0
};
private final ColorMatrix m_colorMatrix = new ColorMatrix(m_bgrToRgbColorTransform);
private final ColorMatrixColorFilter m_colorFilter = new ColorMatrixColorFilter(m_colorMatrix);
...
try{
FREBitmapData as3Bitmap = (FREBitmapData)args[0];
as3Bitmap.acquire();
m_encodingBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
m_canvas = new Canvas(m_encodingBitmap);
m_paint = new Paint();
m_paint.setColorFilter(m_colorFilter);
m_encodingBitmap.copyPixelsFromBuffer(as3BitmapBytes);
as3Bitmap.release();
//
// Convert the bitmap from BGRA to RGBA.
//
m_canvas.drawBitmap(m_encodingBitmap, 0, 0, m_paint);
...
}
Hope that helps ... Timm
I wanted to mask a Bitmap with a black and white alpha mask. My mask image is black and white, the BLACK area means TRANSPARENT and WHITE area means OPAQUE.
What I need is:
When I use this mask image to mask any other image, the area of resultant image should TRANSPARENT if the corresponding area of mask image is BLACK. Otherwise the area of resultant image should be OPAQUE.
I have attached the sample images. Please help me with this guys.
Sample Image:
Sample Image for Masking
What I have tried so far:
The following methods work fine. But they are very slow. I needed some solution that is efficient in terrms of speed and memory than these methods.
First Method:
int width = rgbDrawable.getWidth();
int height = rgbDrawable.getHeight();
if (width != alphaDrawable.getWidth() || height != alphaDrawable.getHeight()) {
throw new IllegalStateException("image size mismatch!");
}
Bitmap destBitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
int[] pixels = new int[width];
int[] alpha = new int[width];
for (int y = 0; y < height; y++)
{
rgbDrawable.getPixels(pixels, 0, width, 0, y, width, 1);
alphaDrawable.getPixels(alpha, 0, width, 0, y, width, 1);
for (int x = 0; x < width; x++)
{
// Replace the alpha channel with the r value from the bitmap.
pixels[x] = (pixels[x] & 0x00FFFFFF) | ((alpha[x] << 8) & 0xFF000000);
}
destBitmap.setPixels(pixels, 0, width, 0, y, width, 1);
}
alphaDrawable.recycle();
rgbDrawable.recycle();
return destBitmap;
Second Method
float[] nlf = new float[] {
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
1, 0, 0, 0, 0};
ColorMatrix sRedToAlphaMatrix = new ColorMatrix(nlf);
ColorMatrixColorFilter sRedToAlphaFilter = new ColorMatrixColorFilter(sRedToAlphaMatrix);
// Load RGB data
Bitmap rgb = rgbDrawable;
// Prepare result Bitmap
Bitmap target = Bitmap.createBitmap(rgb.getWidth(), rgb.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(target);
c.setDensity(Bitmap.DENSITY_NONE);
// Draw RGB data on our result bitmap
c.drawBitmap(rgb, 0, 0, null);
// At this point, we don't need rgb data any more: discard!
rgb.recycle();
rgb = null;
// Load Alpha data
Bitmap alpha = alphaDrawable;
// Draw alpha data on our result bitmap
final Paint grayToAlpha = new Paint();
grayToAlpha.setColorFilter(sRedToAlphaFilter);
grayToAlpha.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
c.drawBitmap(alpha, 0, 0, grayToAlpha);
// Don't need alpha data any more: discard!
alpha.recycle();
alpha = null;
return target;
I'm beating my head against a wall here, and I'm fairly certain I'm doing something stupid, so time to make my stupidity public.
I'm trying to take two images, blend them together into a third image using standard blending algorithms (Hardlight, softlight, overlay, multiply, etc).
Because Android does not have such blend properties build in, I've gone down the path of taking each pixel and combine them using an algorithm. However, the results are garbage. Below is the results of a simple multiply blend (images used, and expected result).
BASE:
BLEND:
EXPECTED RESULT:
GARBAGE RESULT:
Any help would be appreciated. Below is the code, which I've tried to strip out all the "junk", but some may have made it through. I'll clean it up if something isn't clear.
ImageView imageView = (ImageView) findViewById(R.id.ImageView01);
Bitmap base = BitmapFactory.decodeResource(getResources(), R.drawable.base);
Bitmap result = base.copy(Bitmap.Config.RGB_565, true);
Bitmap blend = BitmapFactory.decodeResource(getResources(), R.drawable.blend);
IntBuffer buffBase = IntBuffer.allocate(base.getWidth() * base.getHeight());
base.copyPixelsToBuffer(buffBase);
buffBase.rewind();
IntBuffer buffBlend = IntBuffer.allocate(blend.getWidth() * blend.getHeight());
blend.copyPixelsToBuffer(buffBlend);
buffBlend.rewind();
IntBuffer buffOut = IntBuffer.allocate(base.getWidth() * base.getHeight());
buffOut.rewind();
while (buffOut.position() < buffOut.limit()) {
int filterInt = buffBlend.get();
int srcInt = buffBase.get();
int redValueFilter = Color.red(filterInt);
int greenValueFilter = Color.green(filterInt);
int blueValueFilter = Color.blue(filterInt);
int redValueSrc = Color.red(srcInt);
int greenValueSrc = Color.green(srcInt);
int blueValueSrc = Color.blue(srcInt);
int redValueFinal = multiply(redValueFilter, redValueSrc);
int greenValueFinal = multiply(greenValueFilter, greenValueSrc);
int blueValueFinal = multiply(blueValueFilter, blueValueSrc);
int pixel = Color.argb(255, redValueFinal, greenValueFinal, blueValueFinal);
buffOut.put(pixel);
}
buffOut.rewind();
result.copyPixelsFromBuffer(buffOut);
BitmapDrawable drawable = new BitmapDrawable(getResources(), result);
imageView.setImageDrawable(drawable);
}
int multiply(int in1, int in2) {
return in1 * in2 / 255;
}
After reproducing, I think your issue has to do with manipulating the images in RGB565 mode. As discussed in this post, Bitmaps apparently need to be in ARGB8888 mode to manipulate properly. I first got the expected result on a multiply blend by doing the following:
Resources res = getResources();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap base = BitmapFactory.decodeResource(res, R.drawable.base, options);
Bitmap blend = BitmapFactory.decodeResource(res, R.drawable.blend, options);
// now base and blend are in ARGB8888 mode, which is what you want
Bitmap result = base.copy(Config.ARGB_8888, true);
// Continue with IntBuffers as before...
Converting the Bitmaps to ARGB8888 mode did seem to work for me, at least with the gradient test patterns. However, it you only need to do Screen or Multiply, you might try this as well:
// Same image creation/reading as above, then:
Paint p = new Paint();
p.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));
p.setShader(new BitmapShader(blend, TileMode.CLAMP, TileMode.CLAMP));
Canvas c = new Canvas();
c.setBitmap(result);
c.drawBitmap(base, 0, 0, null);
c.drawRect(0, 0, base.getWidth(), base.getHeight(), p);
With that, you aren't doing the per-pixel calculations, but you are limited to the preset PorterDuff.Modes. In my quick (and dirty) testing, this was the only way I was able to get the blending to work on non-gradient images.
Simple overlay you can do this way (for simplicity it is supposed that bmp1 is equal or bigger than bmp2):
private Bitmap bitmapOverlay(Bitmap bmp1, Bitmap bmp2)
{
Bitmap bmOverlay = Bitmap.createBitmap(bmp1.getWidth(), bmp1.getHeight(), bmp1.getConfig());
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(bmp1, 0, 0, null);
canvas.drawBitmap(bmp2, 0, 0, null);
return bmOverlay;
}
For more complex blending algorithms, maybe you can help yourself with some available Bitmap/Canvas functions.