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
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 building an Android Application and I want to be able to detect black circles. I am using OpenCV3 for Android and I am able to filter out the black colours from my camera feed using the code below. Kindly note that I used the Color-blob-detection example given in the Android SDK and tweaked the code as such:
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
mRgba = inputFrame.rgba();
if (mIsColorSelected) {
Bitmap resultBitmap;
resultBitmap = Bitmap.createBitmap(mRgba.cols(), mRgba.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mRgba, resultBitmap);
//TODO - look for circles
Mat mat = new Mat(resultBitmap.getWidth(), resultBitmap.getHeight(), CvType.CV_8UC1);
Utils.bitmapToMat(resultBitmap, mat);
final Bitmap bitmap;
//TODO - filter out the black only
Mat mHSV = mat;
Mat mHSVThreshed = mat;
Mat mRgba2 = mat;
Imgproc.cvtColor(mat, mHSV, Imgproc.COLOR_BGR2HSV, 0);
Core.inRange(mHSV, new Scalar(0, 0, 0), new Scalar(130, 130, 130), mHSVThreshed);
Imgproc.cvtColor(mHSVThreshed, mat, Imgproc.COLOR_GRAY2BGR, 0);
Imgproc.cvtColor(mat, mRgba2, Imgproc.COLOR_BGR2RGBA, 0);
Imgproc.GaussianBlur(mRgba2, mRgba2, new Size(9, 9), 2, 2);
//this is for displaying purposes only.
//At this point, the image would be black and white, where the white spots are the black detected blobs
// Bitmap bmp = Bitmap.createBitmap(mRgba2.cols(), mRgba2.rows(), Bitmap.Config.ARGB_8888);
//Utils.matToBitmap(mRgba2, bmp);
//bitmap = bmp; //resultBitmap;
//TODO - new circle detection code: this uses the colour filtered Mat
mat = mRgba2;
Imgproc.HoughCircles(mat, circles,
Imgproc.CV_HOUGH_GRADIENT, 1, minDist, 100,
20, 0, 0);
/* get the number of circles detected */
int numberOfCircles = (circles.rows() == 0) ? 0 : circles.cols();
/* draw the circles found on the image */
for (int i = 0; i < numberOfCircles; i++) {
/* get the circle details, circleCoordinates[0, 1, 2] = (x,y,r)
* (x,y) are the coordinates of the circle's center
*/
double[] circleCoordinates = circles.get(0, i);
int x = (int) circleCoordinates[0], y = (int) circleCoordinates[1];
Point center = new Point(x, y);
int radius = (int) circleCoordinates[2];
/* circle's outline */
Imgproc.circle(mRgba2, center, radius, new Scalar(0,
200, 255), 4);
/* circle's center outline */
Imgproc.rectangle(mRgba2, new Point(x - 5, y - 5),
new Point(x + 5, y + 5),
new Scalar(0, 200, 255), -1);
}
Utils.matToBitmap(mRgba2, resultBitmap);
bitmap = resultBitmap;
runOnUiThread(new Runnable() {
#Override
public void run() {
mOpenCvCameraView.disableView();
mOpenCvCameraView.setVisibility(SurfaceView.GONE);
imageView.setVisibility(View.VISIBLE);
imageView.setImageBitmap(bitmap);
}
});
}
return mRgba;
}
What my code does is that it takes a snapshot of the camera feed as a Mat, and then using that Mat, I do some image processing on it and to black out everything except the black colour. The resulting Mat is the mRgba2 variable and I converted to Bitmap and displayed on an ImageView. I displayed this Bitmap to confirm that I am getting the result I want.
After I know that I'm able to filter out the colour I wanted, I then run a GaussianBlur on it then proceed to run HoughCircles. However, when I run the
Imgproc.HoughCircles(mat, circles,
Imgproc.CV_HOUGH_GRADIENT, 1, minDist, 100,
20, 0, 0);
line, I get an
The source image must be 8-bit, single-channel in function CvSeq* cvHoughCircles(CvArr*, void*, int, double, double, double, double, int, int)
Error.
I know that the code wherein I run HoughCircles on a given mat variable works, because I tested it before. Now, changing the mat variable I feed onto it does not, and I wonder what I did differently for the code to not work.
HoughCircles runs only on grayscale (CV8U) images.
Replacing
Imgproc.cvtColor(mat, mRgba2, Imgproc.COLOR_BGR2RGBA, 0);
with
Imgproc.cvtColor(mat, mRgba2, Imgproc.COLOR_BGR2GRAY, 0);
should remove the error.
Then the problem is to detect circles of a given color. This won't pose much of a problem in your case, because you are detecting black circles anyway. Instead of black circles, this will detect dark circles.
If you have dark colors other than black in some of the circles, you can filter them out separately by looking at the pixel values in a different color space.
I am using a few of the stock Android resources, such as ic_menu_camera.png:
The images have a transparent background (desired), but also some transparency in the coloured pixels, too (undesired).
I'm using a ColorMatrixColorFilter to apply a tint to these images and that's working fine, however, the small amount of transparency in the icons is causing the underlying background to bleed through, and fading the colour. I can't figure out a programmatic way to set all coloured pixels to be opaque. Any help?
Current colouring code:
public static void colorImageView(Context context, ImageView imageView, #ColorRes int colorResId) {
Drawable drawable = imageView.getDrawable();
int color = context.getResources().getColor(colorResId);
drawable.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(new float[] {
0, 0, 0, 0, Color.red(color),
0, 0, 0, 0, Color.green(color),
0, 0, 0, 0, Color.blue(color),
0, 0, 0, 1, 0,
})));
}
Current result:
(first icon's source image is opaque, whereas the other 3 have undesired transparency, leading to this off-blue colour)
A very simple approach came to my mind first:
Maybe you could convert the Drawable into a Bitmap and perform the desired pixel operations (like in this answer), e.g:
for(int x = 0; x < bitmap.getWidth(); x++) {
for(int y = 0; y < bitmap.getHeight(); y++) {
int pixel = bitmap.getPixel(x, y);
int r = Color.red(pixel), g = Color.green(pixel), b = Color.blue(pixel);
if (r != 0 || g != 0 || b != 0)
{
pixel.setPixel(x, y, Color.rgb(r, g, b));
}
}
}
to delete any alpha channel value in each pixel with an r-, g- or b-value higher than zero.
But I dont know, how poor or slow this approach works. I think the conversion into a bitmap with pixel operations might be much slower than a ColorMatrixColorFilter.
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.
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;