Guys I have a solution to crate a bitmap from 5 different another ones. However, I want this solution to be faster. I could not find another easy and fast way to do same.
private void createImage() {
// Magic numbers from image files
int numberOfImages = 5;
int imgWidth = 125;
int imgHeight = 300;
int totalwidth = imgWidth * numberOfImages;
int totalheight = imgHeight;
img = Bitmap.createBitmap(totalwidth, totalheight, Bitmap.Config.ARGB_8888);
for (int i = 0; i < numberOfImages; i++) {
Bitmap imgFile = BitmapFactory.decodeResource(context.getResources(), context.getResources().getIdentifier(
"f" + i, "drawable", context.getPackageName()));
for (int x = 0; x < imgWidth; x++) {
for (int y = 0; y < imgHeight; y++) {
int targetX = x + (i * imgWidth);
int targetY = y;
int color = imgFile.getPixel(x, targetY);
img.setPixel(targetX, targetY, color);
}
}
}
img = Bitmap.createScaledBitmap(img, screenWidth, screenHeight, true);
}
You are mostly there. You've created a bitmap that is wide enough for them all.
What you then need to do is draw the bitmaps directly onto it one at a time, but the whole bitmap at once, not pixel by pixel.
You can do this using something call Canvas. Which aids in drawing things onto bitmaps.
img = Bitmap.createBitmap(totalwidth, totalheight, Bitmap.Config.ARGB_8888);
Canvas drawCanvas = new Canvas(img);
for (int i = 0; i < numberOfImages; i++) {
Bitmap imgFile = BitmapFactory.decodeResource(context.getResources(), context.getResources().getIdentifier(
"f" + i, "drawable", context.getPackageName()));
// Draw bitmap onto combo bitmap, at offset x, y.
drawCanvas.drawBitmap(imgFile, i * imgWidth, 0, null);
}
I imagine the Bitmap.getPixels() and Bitmap.setPixels() methods might help.
private void createImage() {
// Magic numbers from image files
int numberOfImages = 5;
int imgWidth = 125;
int imgHeight = 300;
int totalwidth = imgWidth * numberOfImages;
int totalheight = imgHeight;
img = Bitmap.createBitmap(totalwidth, totalheight, Bitmap.Config.ARGB_8888);
int buf[] = new int[totalwidth * totalheight];
for (int i = 0; i < numberOfImages; ++i) {
Bitmap imgFile = BitmapFactory.decodeResource(context.getResources(),
context.getResources().getIdentifier(
"f" + i, "drawable", context.getPackageName()));
imgFile.getPixels(buf, i*imgWidth, totalwidth, 0, 0, imgWidth, imgHeight);
}
img.setPixels(buf, 0, totalwidth, 0, 0, totalwidth, totalheight);
img = Bitmap.createScaledBitmap(img, screenWidth, screenHeight, true);
}
I have not tried it, but based on the documentation, I think it should work. I imagine it will hopefully be considerably faster since we do all the work with only 6 function calls, assuming that under the covers those functions do something much more clever than just looping over the array (such as direct memory copy).
The Doomsknight's answer seems correct to me but you should not do this kind of processes on the main thread for Android. Too many images might hit the 16ms barrier.
You can do this in background handler thread and update the UI Thread after finished.
HandlerThread cropperHandlerThread = new HandlerThread("background_cropper");
cropperHandlerThread.start();
// Do not call getLooper before start handler thread.
Handler cropperHandler = new Handler(cropperHandlerThread.getLooper());
cropperHandler.post(new Runnable() {
#Override
public void run() {
// Create Bitmap Here
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
// Update ImageView here
}
});
}
});
Related
Save segmentation result of Selfie segmentation with ML Kit on Android as A Bitmap with transparent background
I am following this tutorial and code for Selfie segmentation
Here
I have referred this code from the tutorial
ByteBuffer mask = segmentationMask.getBuffer();
int maskWidth = segmentationMask.getWidth();
int maskHeight = segmentationMask.getHeight();
for (int y = 0; y < maskHeight; y++) {
for (int x = 0; x < maskWidth; x++) {
// Gets the confidence of the (x,y) pixel in the mask being in the foreground.
float foregroundConfidence = mask.getFloat();
}
}
Which generates a Mask
Then I have Referred the Sample app Which generates a purple background Mask
Here
using this code
#ColorInt
private int[] maskColorsFromByteBuffer(ByteBuffer byteBuffer) {
#ColorInt int[] colors = new int[maskWidth * maskHeight];
for (int i = 0; i < maskWidth * maskHeight; i++) {
float backgroundLikelihood = 1 - byteBuffer.getFloat();
if (backgroundLikelihood > 0.9) {
colors[i] = Color.argb(128, 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(alpha, 255, 0, 255);
}
}
return colors;
}
Now I want to generate an Image with the original images detected mask and Overlay it on a Transparent Image and save that bitmap for this I am using this code
public Bitmap generateMaskBgImage(Bitmap image, Bitmap bg) {
//Bg is Transparent Png Image.
Bitmap bgBitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), image.getConfig());
for (int y = 0; y < maskHeight; y++) {
for (int x = 0; x < maskWidth; x++) {
int bgConfidence = (int) ((1.0 - maskBuffer.getFloat()) * 255);
int bgPixel = bg.getPixel(x, y);
bgPixel = ColorUtils.setAlphaComponent(bgPixel, bgConfidence);
bgBitmap.setPixel(x, y, bgPixel);
}
}
maskBuffer.rewind();
return bitmapUtils.mergeBitmaps(image, bgBitmap);
}
However it generates an Image with the desired mask but with a Black back ground, How can we save that image with Transparent background.
You can try this (color1 is the color you set in the mask):
private Bitmap performBW(Bitmap originBitmap,Bitmap maskBitmap) {
Bitmap bmOut = Bitmap.createBitmap(originBitmap.getWidth(), originBitmap.getHeight(),
originBitmap.getConfig());
int w = originBitmap.getWidth();
int h = originBitmap.getHeight();
int[] colors = new int[w * h];
int[] colorsMask=new int[maskBitmap.getWidth() * maskBitmap.getHeight()];
originBitmap.getPixels(colors, 0, w, 0, 0, w, h);
maskBitmap.getPixels(colorsMask, 0, w, 0, 0, w, h);
int pos;
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
pos = i * w + j;
if (colorsMask[pos] == color1) colors[pos]=Color.TRANSPARENT;
}
}
bmOut.setPixels(colors, 0, w, 0, 0, w, h);
return bmOut;
}
I have a drawable that represents a white circle with anti-aliasing that needs to be coloured in runtime.
Here's a scaled image of it:
As you can see, there are few semi-transparent pixels.
If I try to color them the fast way (which takes roughly 6-9 ms for 192x192 px drawable), I will have troubles with semi-transparent.
public static void changeBitmapColor(#NonNull Bitmap src, #ColorInt int newColor) {
Paint paint = new Paint();
ColorFilter filter = new PorterDuffColorFilter(newColor, PorterDuff.Mode.SRC_IN);
paint.setColorFilter(filter);
Canvas canvas = new Canvas(src);
canvas.drawBitmap(src, 0, 0, paint);
}
Here's the drawable after being coloured by setting ColorFilter:
If I do it using brute-force algorithm, it takes roughly 100ms to go over all pixels and apply alpha parameter to a new color:
public static void changeBitmapColor2(#NonNull Bitmap src, #ColorInt int newColor) {
int w = src.getWidth();
int h = src.getHeight();
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int color = src.getPixel(x, y);
int alpha = color >>> 24;
if (alpha == 0) {
continue;
}
color = (newColor & 0x00ffffff) | (alpha << 24);
src.setPixel(x, y, color);
}
}
}
The resulting image of 2nd algorithm:
Is there anything I could do with 1st algorithm that it will result in a better quality colouring without sacrificing the performance?
Turns out, the 2nd algorithm had the right idea, but poor implementation. The key was to batch pixel retrieval and set, i.e. using pixel array:
public static void changeBitmapColor2(#NonNull Bitmap src, #ColorInt int newColor) {
int width = src.getWidth();
int height = src.getHeight();
int[] pixels = new int[height * width];
src.getPixels(pixels, 0, width, 0, 0, width, height);
int newColorNoAlpha = newColor & 0x00ffffff;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
int currentPixel = i * width + j;
int color = pixels[currentPixel];
int alpha = color >>> 24;
if (alpha == 0) {
continue;
}
pixels[currentPixel] = newColorNoAlpha | (alpha << 24);
}
}
src.setPixels(pixels, 0, width, 0, 0, width, height);
}
This batching has reduced time to change color of 200x200 image from 100-120ms to 1-4 ms :)
Anyone know how to add subtle noise to grayscale image?
I have a grayscale image that I want to add some subtle black and white noise to. Anyone know how to do this?
I'm currently using this method, but this is just generating random colors and swapping existing pixels with those colors. How can I add some subtle black and white noise to only SOME pixels on a bitmap?
public static Bitmap applyFleaEffect(Bitmap source) {
// get source image size
int width = source.getWidth();
int height = source.getHeight();
int[] pixels = new int[width * height];
// get pixel array from source
source.getPixels(pixels, 0, width, 0, 0, width, height);
// create a random object
Random random = new Random();
int index = 0;
// iteration through pixels
for(int y = 0; y < height; ++y) {
for(int x = 0; x < width; ++x) {
// get current index in 2D-matrix
index = y * width + x;
// get random color
int randColor = Color.rgb(random.nextInt(255),
random.nextInt(255), random.nextInt(255));
// OR
pixels[index] |= randColor;
}
}
// output bitmap
Bitmap bmOut = Bitmap.createBitmap(width, height, source.getConfig());
bmOut.setPixels(pixels, 0, width, 0, 0, width, height);
return bmOut;
}
}
UPDATE: Adding complete code to show grayscale step, and attempted noise step
//First, apply greyscale to make Image black and white
Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, mBitmap.getConfig());
Canvas c = new Canvas(bmpGrayscale);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0);
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
paint.setColorFilter(f);
//Apply noise after grayscale
int[] pixels = new int[width * height];
bmpGrayscale.getPixels(pixels, 0, width, 0, 0, width, height);
// a random object
Random random = new Random();
int index = 0;
// Note: Declare the c and randColor variables outside of the for loops
int co = 0;
int randColor = 0;
// iteration through pixels
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
if (random.nextInt(101) < percentNoise) {
// Skip this iteration a certain percentage of the time
continue;
}
// get current index in 2D-matrix
index = y * width + x;
co = random.nextInt(255);
randColor = Color.rgb(co, co, co);
pixels[index] |= randColor;
}
}
// output bitmap
mBitmap = Bitmap.createBitmap(width, height, bmpGrayscale.getConfig());
mBitmap.setPixels(pixels, 0, width, 0, 0, width, height);
c.drawBitmap(mBitmap, 0, 0, paint);
return bmpGrayscale;
}
First, randColor is not likely to be a gray-scale color with your code. To generate a random, gray-scale color:
int c = random.nextInt(255);
int randColor = Color.rgb(c, c, c);
With that in mind, here is how I would re-write your code (note: adjust the percentNoise parameter to your liking):
public static Bitmap applyFleaEffect(Bitmap source, int percentNoise) {
// get source image size
int width = source.getWidth();
int height = source.getHeight();
int[] pixels = new int[width * height];
// get pixel array from source
source.getPixels(pixels, 0, width, 0, 0, width, height);
// create a random object
Random random = new Random();
int index = 0;
// Note: Declare the c and randColor variables outside of the for loops
int c = 0;
int randColor = 0;
// iterate through pixels
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
if (random.nextInt(101) < percentNoise) {
// Skip this iteration a certain percentage of the time
continue;
}
// get current index in 2D-matrix
index = y * width + x;
// get random color
c = random.nextInt(255);
randColor = Color.rgb(c, c, c);
pixels[index] |= randColor;
}
}
Bitmap bmOut = Bitmap.createBitmap(width, height, source.getConfig());
bmOut.setPixels(pixels, 0, width, 0, 0, width, height);
return bmOut;
}
i hava done with get grayscale value, but i don't know how to use function to convert the grayscale to be binary image. Please help me, here my function code:
public Bitmap toBinary(Bitmap bmpOriginal) {
int width, height, threshold;
height = bmpOriginal.getHeight();
width = bmpOriginal.getWidth();
threshold = 127;
final Bitmap bmpBinary = null;
for(int x = 0; x < width; ++x) {
for(int y = 0; y < height; ++y) {
// get one pixel color
int pixel = bmpOriginal.getPixel(x, y);
//get grayscale value
int gray = (int)(pixel & 0xFF);
//get binary value
if(gray < threshold){
bmpBinary.setPixel(x, y, 0);
} else{
bmpBinary.setPixel(x, y, 255);
}
}
}
return bmpBinary;
}
here my full code:
public class MainActivity extends Activity {
ImageView img;
Button btn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//convert imageview to bitmap
img =(ImageView) findViewById(R.id.imageView1);
BitmapDrawable drawable = (BitmapDrawable) img.getDrawable();
final Bitmap imgbitmap = drawable.getBitmap();
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//convert bitmap to grayscale
Bitmap imgnew;
imgnew = toGrayscale(imgbitmap);
//convert to binary
imgnew = toBinary(imgnew);
//convert bitmap to imageview
ImageView imgbit;
imgbit = (ImageView) findViewById(R.id.imageView2);
imgbit.setImageBitmap(imgnew);
}
});
}
public Bitmap toGrayscale(Bitmap bmpOriginal){
int width, height;
height = bmpOriginal.getHeight();
width = bmpOriginal.getWidth();
Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
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;
}
public Bitmap toBinary(Bitmap bmpOriginal) {
int width, height, threshold;
height = bmpOriginal.getHeight();
width = bmpOriginal.getWidth();
threshold = 127;
final Bitmap bmpBinary = null;
for(int x = 0; x < width; ++x) {
for(int y = 0; y < height; ++y) {
// get one pixel color
int pixel = bmpOriginal.getPixel(x, y);
//get grayscale value
int gray = (int)(pixel & 0xFF);
//get binary value
if(gray < threshold){
bmpBinary.setPixel(x, y, 0);
} else{
bmpBinary.setPixel(x, y, 255);
}
}
}
return bmpBinary;
}
}
First, you get a NullReferenceException because bmpBinary is NULL.
Second, to get one Color chanel you can use int red = Color.red(pixel);
Third, to set a pixel white use bmpBinary.setPixel(x, y, 0xFFFFFFFF);
I modified your code a bit:
public Bitmap toBinary(Bitmap bmpOriginal) {
int width, height, threshold;
height = bmpOriginal.getHeight();
width = bmpOriginal.getWidth();
threshold = 127;
Bitmap bmpBinary = Bitmap.createBitmap(bmpOriginal);
for(int x = 0; x < width; ++x) {
for(int y = 0; y < height; ++y) {
// get one pixel color
int pixel = bmpOriginal.getPixel(x, y);
int red = Color.red(pixel);
//get binary value
if(red < threshold){
bmpBinary.setPixel(x, y, 0xFF000000);
} else{
bmpBinary.setPixel(x, y, 0xFFFFFFFF);
}
}
}
return bmpBinary;
}
An even better way is not to use just the value of one color chanel but a weighted average of red green and blue for example:
int gray = (int)(red * 0.3 + green * 0.59 + blue * 0.11);
I need to convert a 2d integer array (subSrc) to a bitmap. Any solutions?
private Bitmap decimation(Bitmap src){
Bitmap dest = Bitmap.createBitmap(
src.getWidth(), src.getHeight(), src.getConfig());
int bmWidth = src.getWidth();
int bmHeight = src.getHeight();`enter code here`
int[][] subSrc = new int[bmWidth/2][bmWidth/2];
for(int k = 0; k < bmWidth-2; k++){
for(int l = 0; l < bmHeight-2; l++){
subSrc[k][l] = src.getPixel(2*k, 2*l); <---- ??
I looked for a method that received an 2d array (int[][]) and created a Bitmap, and found none, so I wrote one myself:
public static Bitmap bitmapFromArray(int[][] pixels2d){
int width = pixels2d.length;
int height = pixels2d[0].length;
int[] pixels = new int[width * height];
int pixelsIndex = 0;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
pixels[pixelsIndex] = pixels2d[i][j];
pixelsIndex ++;
}
}
return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
}
I also wrote a reverse method:
public static int[][] arrayFromBitmap(Bitmap source){
int width = source.getWidth();
int height = source.getHeight();
int[][] result = new int[width][height];
int[] pixels = new int[width*height];
source.getPixels(pixels, 0, width, 0, 0, width, height);
int pixelsIndex = 0;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
result[i][j] = pixels[pixelsIndex];
pixelsIndex++;
}
}
return result;
}
I hope you find it useful!
you can use
setPixel(int, int, int) or setPixels (int[] pixels, int offset, int stride, int x, int y, int width, int height) methos od bitmap class.
Bitmap dest = Bitmap.createBitmap(
src.getWidth()/2, src.getHeight()/2, src.getConfig());
int bmWidth = src.getWidth();
int bmHeight = src.getHeight();
for(int k = 0; k < bmWidth/2; k++){
for(int l = 0; l < bmHeight/2; l++){
dest.setPixel(k,l,src.getPixel(2*k, 2*l));
But this will be slower i think.
for the 2nd method you uhave to do something like this
int subSrc = new int[(bmWidth/2*)(bmHeight/2)];
for(int k = 0; k < bmWidth-2; k++){
subSrc[k] = src.getPixel(2*(k/bmWidth), 2*(k%bmHeight)); <---- ??
So, you are essentially trying to pull pixels out, do something to them, then make a Bitmap as a result?
The routines expect the pixels to be in a single-dimensional array, so you'll want to put them into the array more like this:
int data[] = new int[size];
data[x + width*y] = pixel(x,y);
...
Then use Bitmap.createBitmap() that accepts the single-dimensional array. You'll want to use the Bitmap.Config for ARGB in your example, since you're using b.getPixel(x,y) which always returns a color in ARGB format.
Bitmap result = Bitmap.createBitmap(data, width, height, Bitmap.Config.ARGB_8888);