how to create image from integer array in Android - android

I'm trying to write a cellular automata program that manipulates integer values in an array and then displays the array as an image.
array of integers --> image --> display on screen
I've tried BitmapFactory.decodeByteArray(), and Bitmap.createBitmap() but all the examples require reading an existing .jpg or .png into a byte[], which is then converted back into a bitmap.
Does anyone have a clear example of building an array from scratch, converting to an image and then displaying? Even the simplest example of an entirely blue square 50x50 pixels would be helpful.
If BitmapFactory.decodeByteArray() is not the best option, I'm open to any alternatives.
Thanks!
my code so far, from an onClick() method:
display = (ImageView) findViewById(R.id.imageView1);
Bitmap bMap = null;
int w = 50, h = 50; // set width = height = 50
byte[] input = new byte[w * h]; // initialize input array
for (int y = 0; y < h; y++) { // fill input with blue
for (int x = 0; x < w; x++) {
input[y * w + x] = (byte) Color.BLUE;
}
}
bMap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length, Bitmap.Config.ARGB_8888); // convert byte array to bitmap
display.setImageBitmap(bMap); // post bitmap to imageview

// You are using RGBA that's why Config is ARGB.8888
bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
// vector is your int[] of ARGB value .
bitmap.copyPixelsFromBuffer(makeBuffer(vector, vector.length));
private IntBuffer makeBuffer(int[] src, int n) {
IntBuffer dst = IntBuffer.allocate(n*n);
for (int i = 0; i < n; i++) {
dst.put(src);
}
dst.rewind();
return dst;
}
creating an empty bitmap and drawing though canvas in android

Related

PixelCopy on pre-Nougat devices [duplicate]

In android, I get an Image object from here https://inducesmile.com/android/android-camera2-api-example-tutorial/ this camera tutorial. But I want to now loop through the pixel values, does anyone know how I can do that? Do I need to convert it to something else and how can I do that?
Thanks
If you want to loop all throughout the pixel then you need to convert it first to Bitmap object. Now since what I see in the source code that it returns an Image, you can directly convert the bytes to bitmap.
Image image = reader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.capacity()];
buffer.get(bytes);
Bitmap bitmapImage = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, null);
Then once you get the bitmap object, you can now iterate through all of the pixels.
YuvToRgbConverter is useful for conversion from Image to Bitmap.
https://github.com/android/camera-samples/blob/master/Camera2Basic/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt
Usage sample.
val bmp = Bitmap.createBitmap(image.width, image.height, Bitmap.Config.ARGB_8888)
yuvToRgbConverter.yuvToRgb(image, bmp)
Actually you have two questions in one
1) How do you loop throw android.media.Image pixels
2) How do you convert android.media.image to Bitmap
The 1-st is easy. Note that the Image object that you get from the camera, it's just a YUV frame, where Y, and U+V components are in different planes. In many Image Processing cases you need only the Y plane, that means the gray part of the image. To get it I suggest code like this:
Image.Plane[] planes = image.getPlanes();
int yRowStride = planes[0].getRowStride();
byte[] yImage = new byte[yRowStride];
planes[0].getBuffer().get(yImage);
The yImage byte buffer is actually the gray pixels of the frame.
In same manner you can get the U+V parts to. Note that they can be U first, and V after, or V and after it U, and maybe interlived (that is the common case case with Camera2 API). So you get UVUV....
For debug purposes, I often write the frame to a file, and trying to open it with Vooya app (Linux) to check the format.
The 2-th question is a little bit more complex.
To get a Bitmap object I found some code example from TensorFlow project here. The most interesting functions for you is "convertImageToBitmap" that will return you with RGB values.
To convert them to a real Bitmap do the next:
Bitmap rgbFrameBitmap;
int[] cachedRgbBytes;
cachedRgbBytes = ImageUtils.convertImageToBitmap(image, cachedRgbBytes, cachedYuvBytes);
rgbFrameBitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
rgbFrameBitmap.setPixels(cachedRgbBytes,0,image.getWidth(), 0, 0,image.getWidth(), image.getHeight());
Note: There is more options of converting YUV to RGB frames, so if you need the pixels value, maybe Bitmap is not the best choice, as it may consume more memory than you need, to just get the RGB values
Java Conversion Method
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
.build();
imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this), new ImageAnalysis.Analyzer() {
#Override
public void analyze(#NonNull ImageProxy image) {
// call toBitmap function
Bitmap bitmap = toBitmap(image);
image.close();
}
});
private Bitmap bitmapBuffer;
private Bitmap toBitmap(#NonNull ImageProxy image) {
if(bitmapBuffer == null){
bitmapBuffer = Bitmap.createBitmap(image.getWidth(),image.getHeight(),Bitmap.Config.ARGB_8888);
}
bitmapBuffer.copyPixelsFromBuffer(image.getPlanes()[0].getBuffer());
return bitmapBuffer;
}
https://docs.oracle.com/javase/1.5.0/docs/api/java/nio/ByteBuffer.html#get%28byte[]%29
According to the java docs: The buffer.get method transfers bytes from this buffer into the given destination array. An invocation of this method of the form src.get(a) behaves in exactly the same way as the invocation
src.get(a, 0, a.length)
I assume you have YUV (YUV (YUV_420_888) Image provided by Camera. Using this interesting How to use YUV (YUV_420_888) Image in Android tutorial I can propose following solution to convert Image to Bitmap.
Use this to convert YUV Image to Bitmap:
private Bitmap yuv420ToBitmap(Image image, Context context) {
RenderScript rs = RenderScript.create(SpeedMeasurementActivity.this);
ScriptIntrinsicYuvToRGB script = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
// Refer the logic in a section below on how to convert a YUV_420_888 image
// to single channel flat 1D array. For sake of this example I'll abstract it
// as a method.
byte[] yuvByteArray = image2byteArray(image);
Type.Builder yuvType = new Type.Builder(rs, Element.U8(rs)).setX(yuvByteArray.length);
Allocation in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);
Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs))
.setX(image.getWidth())
.setY(image.getHeight());
Allocation out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);
// The allocations above "should" be cached if you are going to perform
// repeated conversion of YUV_420_888 to Bitmap.
in.copyFrom(yuvByteArray);
script.setInput(in);
script.forEach(out);
Bitmap bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
out.copyTo(bitmap);
return bitmap;
}
and a supportive function to convert 3 Planes YUV image to 1 dimesional byte array:
private byte[] image2byteArray(Image image) {
if (image.getFormat() != ImageFormat.YUV_420_888) {
throw new IllegalArgumentException("Invalid image format");
}
int width = image.getWidth();
int height = image.getHeight();
Image.Plane yPlane = image.getPlanes()[0];
Image.Plane uPlane = image.getPlanes()[1];
Image.Plane vPlane = image.getPlanes()[2];
ByteBuffer yBuffer = yPlane.getBuffer();
ByteBuffer uBuffer = uPlane.getBuffer();
ByteBuffer vBuffer = vPlane.getBuffer();
// Full size Y channel and quarter size U+V channels.
int numPixels = (int) (width * height * 1.5f);
byte[] nv21 = new byte[numPixels];
int index = 0;
// Copy Y channel.
int yRowStride = yPlane.getRowStride();
int yPixelStride = yPlane.getPixelStride();
for(int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
nv21[index++] = yBuffer.get(y * yRowStride + x * yPixelStride);
}
}
// Copy VU data; NV21 format is expected to have YYYYVU packaging.
// The U/V planes are guaranteed to have the same row stride and pixel stride.
int uvRowStride = uPlane.getRowStride();
int uvPixelStride = uPlane.getPixelStride();
int uvWidth = width / 2;
int uvHeight = height / 2;
for(int y = 0; y < uvHeight; ++y) {
for (int x = 0; x < uvWidth; ++x) {
int bufferIndex = (y * uvRowStride) + (x * uvPixelStride);
// V channel.
nv21[index++] = vBuffer.get(bufferIndex);
// U channel.
nv21[index++] = uBuffer.get(bufferIndex);
}
}
return nv21;
}
start with the imageProxy from the analyizer
#Override
public void analyze(#NonNull ImageProxy imageProxy)
{
Image mediaImage = imageProxy.getImage();
if (mediaImage != null)
{
toBitmap(mediaImage);
}
imageProxy.close();
}
Then convert to a bitmap
private Bitmap toBitmap(Image image)
{
if (image.getFormat() != ImageFormat.YUV_420_888)
{
throw new IllegalArgumentException("Invalid image format");
}
byte[] nv21b = yuv420ThreePlanesToNV21BA(image.getPlanes(), image.getWidth(), image.getHeight());
YuvImage yuvImage = new YuvImage(nv21b, ImageFormat.NV21, image.getWidth(), image.getHeight(), null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
yuvImage.compressToJpeg (new Rect(0, 0,
yuvImage.getWidth(),
yuvImage.getHeight()),
mQuality, baos);
mFrameBuffer = baos;
//byte[] imageBytes = baos.toByteArray();
//Bitmap bm = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
return null;
}
Here's the static function that worked for me
public static byte [] yuv420ThreePlanesToNV21BA(Plane[] yuv420888planes, int width, int height)
{
int imageSize = width * height;
byte[] out = new byte[imageSize + 2 * (imageSize / 4)];
if (areUVPlanesNV21(yuv420888planes, width, height)) {
// Copy the Y values.
yuv420888planes[0].getBuffer().get(out, 0, imageSize);
ByteBuffer uBuffer = yuv420888planes[1].getBuffer();
ByteBuffer vBuffer = yuv420888planes[2].getBuffer();
// Get the first V value from the V buffer, since the U buffer does not contain it.
vBuffer.get(out, imageSize, 1);
// Copy the first U value and the remaining VU values from the U buffer.
uBuffer.get(out, imageSize + 1, 2 * imageSize / 4 - 1);
}
else
{
// Fallback to copying the UV values one by one, which is slower but also works.
// Unpack Y.
unpackPlane(yuv420888planes[0], width, height, out, 0, 1);
// Unpack U.
unpackPlane(yuv420888planes[1], width, height, out, imageSize + 1, 2);
// Unpack V.
unpackPlane(yuv420888planes[2], width, height, out, imageSize, 2);
}
return out;
}
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
1-Store the path to the image file as a string variable. To decode the content of an image file, you need the file path stored within your code as a string. Use the following syntax as a guide:
String picPath = "/mnt/sdcard/Pictures/mypic.jpg";
2-Create a Bitmap Object And Use BitmapFactory:
Bitmap picBitmap;
Bitmap picBitmap = BitmapFactory.decodeFile(picPath);

Converting YUV image to RGB results in greenish picture

I am getting an Image in the YUV_420_888 format as the result of a capture using the Camera2 APIs. I need to convert the image to the RGB format, but the colors of the resulting image are wrong.
This is the function that performs the conversion, using OpenCV:
#TargetApi(Build.VERSION_CODES.KITKAT)
public static Bitmap createBitmapFromYUV420(Image image) {
Image.Plane[] planes = image.getPlanes();
byte[] imageData = new byte[image.getWidth() * image.getHeight() * ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888) / 8];
ByteBuffer buffer = planes[0].getBuffer();
int lastIndex = buffer.remaining();
buffer.get(imageData, 0, lastIndex);
int pixelStride = planes[1].getPixelStride();
for (int i = 1; i < planes.length; i++) {
buffer = planes[i].getBuffer();
byte[] planeData = new byte[buffer.remaining()];
buffer.get(planeData);
for (int j = 0; j < planeData.length; j += pixelStride) {
imageData[lastIndex++] = planeData[j];
}
}
Mat yuvMat = new Mat(image.getHeight() + image.getHeight() / 2, image.getWidth(), CvType.CV_8UC1);
yuvMat.put(0, 0, imageData);
Mat rgbMat = new Mat();
Imgproc.cvtColor(yuvMat, rgbMat, Imgproc.COLOR_YUV420p2RGBA);
Bitmap bitmap = Bitmap.createBitmap(rgbMat.cols(), rgbMat.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(rgbMat, bitmap);
return bitmap;
}
I think that the way the bytes from the 3 planes are appended into the byte array is correct, so perhaps the error is somewhere else?
SOLVED
Apparently, there is a bug in Android API 21 that causes the U and V arrays to be full of 0 except for a few bytes, resulting in a green image. This issue has been fixed with API 22.

Replacing a color in a Bitmap

I have images which I display in my app. They are downloaded from the web. These images are pictures of objects on an almost-white background. I want this background to be white (#FFFFFF). I figure, if I look at pixel 0,0 (which should always be off-white), I can get the color value and replace every pixel in the image having that value with white.
This question has been asked before and the answer seems to be this:
int intOldColor = bmpOldBitmap.getPixel(0,0);
Bitmap bmpNewBitmap = Bitmap.createBitmap(bmpOldBitmap.getWidth(), bmpOldBitmap.getHeight(), Bitmap.Config.RGB_565);
Canvas c = new Canvas(bmpNewBitmap);
Paint paint = new Paint();
ColorFilter filter = new LightingColorFilter(intOldColor, Color.WHITE);
paint.setColorFilter(filter);
c.drawBitmap(bmpOriginal, 0, 0, paint);
However, this isn't working.
After running this code, the entire image seems to be the color I was wanting to remove. As in, the entire image is 1 solid color now.
I was also hoping to not have to loop through every pixel in the entire image.
Any ideas?
Here is a method I created for you to replace a specific color for the one you want. Note that all the pixels will get scanned on the Bitmap and only the ones that are equal will be replaced for the one you want.
private Bitmap changeColor(Bitmap src, int colorToReplace, int colorThatWillReplace) {
int width = src.getWidth();
int height = src.getHeight();
int[] pixels = new int[width * height];
// get pixel array from source
src.getPixels(pixels, 0, width, 0, 0, width, height);
Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
int A, R, G, B;
int pixel;
// iteration through pixels
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
// get current index in 2D-matrix
int index = y * width + x;
pixel = pixels[index];
if(pixel == colorToReplace){
//change A-RGB individually
A = Color.alpha(colorThatWillReplace);
R = Color.red(colorThatWillReplace);
G = Color.green(colorThatWillReplace);
B = Color.blue(colorThatWillReplace);
pixels[index] = Color.argb(A,R,G,B);
/*or change the whole color
pixels[index] = colorThatWillReplace;*/
}
}
}
bmOut.setPixels(pixels, 0, width, 0, 0, width, height);
return bmOut;
}
I hope that helped :)

How do I convert a Bitmap with black pixels to another color in Android?

I've got a Bitmap with some transparent pixels and rest is mainly black (some black pixels possibly have a few sem-transparent pixels).
I need to re-use these bitmaps and want to be able to essentially create a Mask out of this bitmap at runtime and then try and blend with a block of another color (like Red, green etc) so that the end result is the same image but with red color (and those pixels which were semi-transparent black pixels turn into semi-transparent red pixels).
I've tried all sorts of color filters and xfermodes but have not been able to figure out. Please help!
If you doesn't need high speed, you can use simple solution by manually blend pixels.
final Bitmap bmp = /* there your bitmap */;
int w = bmp.getWidth();
int h = bmp.getHeight();
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int color = bmp.getPixel(x, y);
// Shift your alpha component value to the red component's.
color = (color << 24) & 0xFF000000;
bmp.setPixel(x, y, color);
}
}
If you need more effective processing, you must use (at least) getPixels method or, more preferable, native processing.
public void changeColor(Bitmap myBitmap) {
int [] allpixels = new int [myBitmap.getHeight()*myBitmap.getWidth()];
myBitmap.getPixels(allpixels, 0, myBitmap.getWidth(), 0, 0, myBitmap.getWidth(), myBitmap.getHeight());
for(int i = 0; i < allpixels.length; i++)
{
if(allpixels[i] == Color.BLACK)
{
allpixels[i] = Color.RED;
}
}
myBitmap.setPixels(allpixels, 0, myBitmap.getWidth(), 0, 0, myBitmap.getWidth(), myBitmap.getHeight());
}

How to display bitmap pixel by pixel in Android?

I am trying to display a bitmap image pixel by pixel (which explicitly means with some delay).
For that I am using two "for-loops", but it prints only a single row of pixels...
My code:
Button start = (Button) findViewById(R.id.start);
start.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Toast.makeText(getBaseContext(), "Printing...", Toast.LENGTH_SHORT).show();
iImageArray = new int[bMap.getWidth()* bMap.getHeight()]; //initializing the array for the image size
bMap.getPixels(iImageArray, 0, bMap.getWidth(), 0, 0, bMap.getWidth(), bMap.getHeight()); //copy pixel data from the Bitmap into the 'intArray' array
//Canvas canvas = new Canvas (bMap);
//replace the red pixels with yellow ones
for (int i=0; i < bMap.getHeight(); i++) {
for(int j=0; j<bMap.getWidth(); j++) {
iImageArray[j] = 0xFFFFFFFF;
}
}
bMap = Bitmap.createBitmap(iImageArray, bMap.getWidth(), bMap.getHeight(), Bitmap.Config.ARGB_8888);//Initialize the bitmap, with the replaced color
image.setImageBitmap(bMap);
//canvas.drawPoints(iImageArray, 0, bMap.getHeight()*bMap.getWidth(), paint);
}
});
And I want to print the bitmap in grayscale and for that I found this code...
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;
}
It is iImageArray[i] = 0xFFFFFFFF; that I have to test, not the actual grayscale value...
Your code looks basically correct except for your inner loop. Your comment says that you're setting them to yellow, but you're actually storing white. The loop will only affect the first row of pixels because your array index has a range of 0 to width-1. You can either calculate the index by multiplying (i*width+j) or keep a counter that increments.
Original Version:
//replace the red pixels with yellow ones
for (int i=0; i < bMap.getHeight(); i++)
{
for(int j=0; j<bMap.getWidth(); j++)
{
iImageArray[j] = 0xFFFFFFFF;
}
}
Fixed Version:
//replace the red pixels with yellow ones
int iWidth = bMap.getWidth();
for (int i=0; i < bMap.getHeight(); i++)
{
for(int j=0; j<bMap.getWidth(); j++)
{
iImageArray[(i*iWidth)+j] = 0xFF00FFFF; // actual value of yellow
}
}
I also made a program that reads the RGB colors of every pixels from an image.
Here is my code to read:
int orgWidth = bmp.getWidth();
int orgHeight = bmp.getHeight();
//define the array size
int[] pixels = new int[orgWidth * orgHeight];
bmp.getPixels(pixels, 0, orgWidth, 0, 0, orgWidth, orgHeight);
for (int y = 0; y < orgHeight; y++){
for (int x = 0; x < orgWidth; x++){
}
}
This is how my array works.
I made a question for ranged pixel check last week, however I also fixed it by my self :)
Optimize Color range check with nested for loop
EDIT:
Now I see, sorry.
at: iImageArray[j] = 0xFFFFFFFF;
also add iImageArray[i] = 0xFFFFFFFF;
then it should work

Categories

Resources