in my application I have to work on the individual pixels of an image.
first of all, I give the image in grayscale with this code
private Bitmap BiancoNero(Bitmap originalBitmap) {
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0);
ColorMatrixColorFilter colorMatrixFilter = new ColorMatrixColorFilter(
colorMatrix);
Bitmap blackAndWhiteBitmap = originalBitmap.copy(
Bitmap.Config.ARGB_8888, true);
Paint paint = new Paint();
paint.setColorFilter(colorMatrixFilter);
Canvas canvas = new Canvas(blackAndWhiteBitmap);
canvas.drawBitmap(blackAndWhiteBitmap, 0, 0, paint);
return blackAndWhiteBitmap;
}
after that, drawing a matrix with only 0 and 1 (0 white, 1 black)
with the following control
if(colore==-1)
matrice[i][j] = 0; //zero is for white
else if(colore == -16777216)
matrice[i][j] = 1; //black pixel
else
matrice[i][j] =0; //other shades of gray
How do I get to pick up the nuances of dark gray?
that is, when we consider white 0% and black 100%, I want to find all the colors that are above 66%
I just put in the if> color = 5592405 (which is equivalent to -16777216 / 3 )
would it work?
If your matrix is gray as you say then R = G = B values.
Let's take the least significant "byte" and test based on it.
int c = colore & 0xFF;
c supposed to be a value between 0 and 255 inclusive.
66%:
if ((float)c > 255.0f*0.66f)
it's easier to work with small numbers. try it and post results.
Related
I would like to remove a background from an image with a person in order to use this image in some other part of my android app. I have applied Google ML Kit - Selfie Segmentation and receive segmentation Mask as a result. What I want to do now is - save the image without a background to a device so I can then use it in other parts of my app.
Original image:
Image after Applied Selfie Segmentation:
My problem is I need to somehow apply values from segmentationMask (256 * 256) to the Bitmap where I will remove background and save new Bitmap (with values from segmentation mask on only those pixels that are currently blue) with pixels from segmentation mask. Would anyone be so king and point me in the direction? Should I try to achieve this with Matrix ?? Currently I am drawind this image on jetpack compose Canvas with drawPoints comand in DrawScope.
There is an enableRawSizeMask API on the SelfieSegmenterOptions.Builder. If you don't set it, the returned mask should have the same dimension as the input image.
Then a naive approach would be: Iterating through the result mask to determine whether a certain pixel should be kept, if not, call setPixel method on the original Bitmap to set the background pixel to transparent (or whatever color you want).
This is for sure not the most performant way, but maybe a good start.
Maybe you can try it,this is my way
private void initUI(){
ImageView imageView=findViewById(R.id.iv);
Paint paint=new Paint();
paint.setAntiAlias(true);
SelfieSegmenterOptions options=new SelfieSegmenterOptions.Builder()
.setDetectorMode(SelfieSegmenterOptions.STREAM_MODE)
//.enableRawSizeMask()
.build();
Segmenter segmenter = Segmentation.getClient(options);
Bitmap bitmap= BitmapFactory.decodeResource(getResources(), R.drawable.mv1);
InputImage image = InputImage.fromBitmap(bitmap, 0);
segmenter.process(image).addOnSuccessListener(segmentationMask -> {
ByteBuffer mask = segmentationMask.getBuffer();
int maskWidth = segmentationMask.getWidth();
int maskHeight = segmentationMask.getHeight();
Bitmap background=Bitmap.createBitmap(maskColorsFromByteBuffer(mask,maskWidth,maskHeight),maskWidth,maskHeight, Bitmap.Config.ARGB_8888);
//创建一个bitmap
Bitmap foreground=Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888); //裁剪后的图像
Canvas canvas=new Canvas(foreground);
//绘制扣出来的背景
canvas.drawBitmap(background,0,0,paint);
//设置混合模式
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
//绘制原图
canvas.drawBitmap(bitmap,0,0,paint);
imageView.setImageBitmap(foreground);
}).addOnFailureListener(e -> {
// Task failed with an exception
// ...
});
}
private int[] maskColorsFromByteBuffer(ByteBuffer byteBuffer,int maskWidth,int maskHeight) {
#ColorInt int[] colors = new int[maskWidth * maskHeight];
for (int i = 0; i < maskWidth * maskHeight; i++) {
float backgroundLikelihood = 1 - byteBuffer.getFloat();
if (backgroundLikelihood > 0.9) { //128
colors[i] = Color.argb(255, 255, 0, 255);
//colors[i] = Color.argb(0, 255, 0, 255);
} else if (backgroundLikelihood > 0.2) {
// Linear interpolation to make sure when backgroundLikelihood is 0.2, the alpha is 0 and
// when backgroundLikelihood is 0.9, the alpha is 128.
// +0.5 to round the float value to the nearest int.
int alpha = (int) (182.9 * backgroundLikelihood - 36.6 + 0.5);
colors[i] = Color.argb(255, 255, 0, 255);
}
}
return colors;
}
I am new to this site, and I come with a question about Android.
Is there any way to convert a Bitmap to grayscale? I know how to draw a grayscale bitmap (using canvas operations: http://www.mail-archive.com/android-developers#googlegroups.com/msg38890.html) but I really need The actual bitmap in gray colors (or at least something that could be converted to a bitmap later on).
Do I have to implement it by hand (pixel by pixel operations)?
I've searched a lot, and still could not find. Anyone knows a easy/efficient way to do it?
Thanks a lot!
OH, yes, it does.
I was using it wrong, thanks for pointing it out to me.
(Sorry for the useless question)
Here is the end code (heavily based on the one linked) since it may help someone:
public Bitmap toGrayscale(Bitmap bmpOriginal)
{
int width, height;
height = bmpOriginal.getHeight();
width = bmpOriginal.getWidth();
Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmpGrayscale);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0);
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
paint.setColorFilter(f);
c.drawBitmap(bmpOriginal, 0, 0, paint);
return bmpGrayscale;
}
Any remarks or comments on it are very welcome.
Thanks
If you are going to show that Bitmap on ImageView. Then Instead of converting Bitmap to Gray Scale, you can try below code:
ColorMatrix matrix = new ColorMatrix();
matrix.setSaturation(0);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
imageview.setColorFilter(filter);
For reference
Isn't that exactly what the code you're linking to does? It takes a color bitmap ("bmp"), creates a duplicate bitmap ("bm"), and then draws the color bitmap into "bm" using the filter to turn it into grayscale. From that point on, you can use "bm" as an actual grayscale bitmap and do whatever you want to do with it.
You'd need to tweak the sample a bit (it's using hard-coded sizes, you may want to just clone the size of the original bitmap), but other than that, this seems to be as ready-to-use as it gets, depending on what you want.
I'd like to mention that with this approach one important aspect must be taken in account. BitMap's on Android are stored in the NativeHeap. By just "creating bitmaps", you'll eventually clog the memory, getting an OutOfMemoryException (OOM).
Therefor, the bitmap must always be .recycled().
Here's a more efficient way, which I've made to support all versions of Android:
// https://xjaphx.wordpress.com/2011/06/21/image-processing-grayscale-image-on-the-fly/
#JvmStatic
fun getGrayscaledBitmapFallback(src: Bitmap, redVal: Float = 0.299f, greenVal: Float = 0.587f, blueVal: Float = 0.114f): Bitmap {
// create output bitmap
val bmOut = Bitmap.createBitmap(src.width, src.height, src.config)
// pixel information
var A: Int
var R: Int
var G: Int
var B: Int
var pixel: Int
// get image size
val width = src.width
val height = src.height
// scan through every single pixel
for (x in 0 until width) {
for (y in 0 until height) {
// get one pixel color
pixel = src.getPixel(x, y)
// retrieve color of all channels
A = Color.alpha(pixel)
R = Color.red(pixel)
G = Color.green(pixel)
B = Color.blue(pixel)
// take conversion up to one single value
B = (redVal * R + greenVal * G + blueVal * B).toInt()
G = B
R = G
// set new pixel color to output bitmap
bmOut.setPixel(x, y, Color.argb(A, R, G, B))
}
}
// return final image
return bmOut
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
#JvmStatic
fun getGrayscaledBitmap(context: Context, src: Bitmap): Bitmap {
// https://gist.github.com/imminent/cf4ab750104aa286fa08
// https://en.wikipedia.org/wiki/Grayscale
val redVal = 0.299f
val greenVal = 0.587f
val blueVal = 0.114f
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
return getGrayscaledBitmapFallback(src, redVal, greenVal, blueVal)
val render = RenderScript.create(context)
val matrix = Matrix4f(floatArrayOf(-redVal, -redVal, -redVal, 1.0f, -greenVal, -greenVal, -greenVal, 1.0f, -blueVal, -blueVal, -blueVal, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f))
val result = src.copy(src.config, true)
val input = Allocation.createFromBitmap(render, src, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT)
val output = Allocation.createTyped(render, input.type)
// Inverts and do grayscale to the image
#Suppress("DEPRECATION")
val inverter =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
ScriptIntrinsicColorMatrix.create(render)
else
ScriptIntrinsicColorMatrix.create(render, Element.U8_4(render))
inverter.setColorMatrix(matrix)
inverter.forEach(input, output)
output.copyTo(result)
src.recycle()
render.destroy()
return result
}
I have the following image:
Now I’d like to filter this image to different colors at runtime. E.g. I want to be able to generate a green version of it, where the lower part is of a darker green, and the top-left part is lighter green.
How can I do this? Is this even possible?
I have tried filtering the bitmap like as follows:
private static Bitmap applyColorFilter(Bitmap source, int color) {
Bitmap empty = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight());
Canvas c = new Canvas(empty);
Paint p = new Paint();
PorterDuffColorFilter pdcf = new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN);
p.setColorFilter(pcdf);
c.drawBitmap(empty, 0, 0, p);
source.recycle();
return empty;
}
I have tried all of PorterDuff.Modes, but none of them really works. Sometimes (e.g. SRC_IN) I see the circle totally filled with the new color, without any shading. In other cases I see a square.
I have thought about generating a source image with no color itself, and to render its shading with an alpha channel. However, I’m not sure that would work. I want the resulting image to be fully opaque (apart from the outer shadow you can see above).
I have found a solution using ColorMatrixColorFilter.
// Take values from the destination color
int destA = (color >> 24);
int destR = (color >> 16) & 0xff;
int destG = (color >> 8) & 0xff;
int destB = color & 0xff;
// Give the same weight to source R,G,B, thus grasping the intensity
// -> the shading overlay
float mean = 0.33f;
ColorMatrixColorFilter cm = new ColorMatrixColorFilter(
new float[]{
mean,mean,mean,0,destR,
mean,mean,mean,0,destG,
mean,mean,mean,0,destB,
0,0,0,1,destA }
);
Paint p = new Paint();
p.setColorFilter(cm);
...
Inevitably, this slightly changes (brightens) the destination color, so I’ll wait some days for a better solution. For my input image, I have better results by lowering the mean value to approx. 0.2.
The same result could probably be obtained with a LightningColorFilter.
I have this piece of code that takes the bitmap of a CameraPreview from a TextureView and renders it on a ImageView.
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// Invoked every time there's a new Camera preview frame
bmp = mTextureView.getBitmap();
bmp2 = bmp.copy(bmp.getConfig(),true);
for(int x=0;x<bmp.getWidth();x++){
for(int y=0;y<bmp.getHeight();y++){
//Log.i("Pixel RGB (Int)", Integer.toString(bmp.getPixel(x,y)));
if(bmp.getPixel(x,y) < -8388608){
bmp2.setPixel(x,y,Color.WHITE);
}else{
bmp2.setPixel(x,y,Color.BLACK);
}
}
}
mImageView.setImageBitmap(bmp2);
}
So basically I will be applying real-time image-processing on whatever the camera shows. For now it just back and whites pixels. It is a bit slow now, and the bitmap has only a width and height of ~250 pixels.
Is this the recommended way of doing this ?
To filter bitmaps efficiently you can use ColorMatrixColorFilter. For example to make your image black & white use this code:
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0);
float m = 255f;
float t = -255*1.2f;
ColorMatrix threshold = new ColorMatrix(new float[] {
m, 0, 0, 1, t,
0, m, 0, 1, t,
0, 0, m, 1, t,
0, 0, 0, 1, 0
});
// Convert to grayscale, then scale and clamp
colorMatrix.postConcat(threshold);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
imageView.setColorFilter(filter);
Basically you have to transform the color range so values equal to (color) and (color+1) are (0) and (1). That's why I'm multiplying color by 255 and shifting. You may want to play with these parameters to get the right result.
Check out the slides here: http://chiuki.github.io/android-shaders-filters/#/16
CODE:
public final void SetBitmapImage(Bitmap paramBitmap, int color, float percentage)
{
float f = percentage * -360.0F;
int i = paramBitmap.getWidth();
int j = paramBitmap.getHeight();
Paint localPaint1 = new Paint(ANTI_ALIAS_FLAG);
Paint localPaint2 = new Paint(ANTI_ALIAS_FLAG);
Paint localPaint3 = new Paint(ANTI_ALIAS_FLAG);
Bitmap localBitmap1 = Bitmap.createBitmap(i, j, Bitmap.Config.ARGB_8888);
Canvas localCanvas1 = new Canvas(localBitmap1);
localCanvas1.rotate(-90.0F, i / 2.0F, j / 2.0F);
RectF localRectF = new RectF(0.0F, 0.0F, i, j);
localPaint1.setStrokeWidth(2.0F);
localPaint1.setStyle(Paint.Style.FILL);
localPaint2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
Bitmap localBitmap2 = paramBitmap; // this should include color I think but don't know how
localCanvas1.drawArc(localRectF, 0.0F, f, true, localPaint1);
Bitmap localBitmap3 = Bitmap.createBitmap(localBitmap1.getWidth(), localBitmap1.getHeight(), Bitmap.Config.ARGB_8888);
Canvas localCanvas2 = new Canvas(localBitmap3);
localCanvas2.drawBitmap(paramBitmap, 0.0F, 0.0F, localPaint3);
localCanvas2.drawBitmap(localBitmap1, 0.0F, 0.0F, localPaint2);
Bitmap localBitmap4 = Bitmap.createBitmap(localBitmap2.getWidth(), localBitmap2.getHeight(), localBitmap2.getConfig());
Canvas localCanvas3 = new Canvas(localBitmap4);
localCanvas3.drawBitmap(localBitmap2, new Matrix(), null);
localCanvas3.drawBitmap(localBitmap3, 0.0F, 0.0F, null);
localBitmap2.recycle();
localBitmap3.recycle();
localBitmap1.recycle();
setImageBitmap(localBitmap4);
}
but the image is still the same. I want that 70% of image stay white, 30% red.
I think that a problem is with Bitmap localBitmap2.
Bitmap localBitmap2 = paramBitmap; // this should include color I think but don't know how
Should be something like:
Bitmap localBitmap2 = Something(paramBitmap, color); which will set the color
In android, when you set a hex code, the first 6 digits following the # is the normal code, and there is an optional 2 digits after for transparency.
#000000 = black
#00000000 = fully transparent
Given that that last two digits need to be in hex format, you need to choose your range between 0 - 255, then convert it back to hex.
TIP: You can do this using windows calculator in 'programmer' mode. :)
Examples:
30% opacity: 255*0.70 = 178. 178 converted to hex = B2.
So add B2 to the end of whatever color code you have, and it will be 30% opaque.
#000000B2
Hopefully this helps.