Can someone tell me why I am getting this error when splitting a bitmap.
Code:
public static List<Bitmap> ScambleImage(Bitmap image, int rows, int cols){
List<Bitmap> scambledImage = new ArrayList<Bitmap>();
int chunkWidth = image.getWidth(); //cols
int chunkHeight = image.getHeight(); //rows
int finalSize = chunkWidth/rows;
Bitmap bMapScaled = Bitmap.createScaledBitmap(image, chunkWidth, chunkHeight, true);
int yCoord = 0;//The y coordinate of the first pixel in source
for(int x = 0; x < rows; x++){
int xCoord = 0;//The x coordinate of the first pixel in source
for(int y = 0; y < cols; y++){
scambledImage.add(Bitmap.createBitmap(bMapScaled, xCoord, yCoord, finalSize, finalSize));
xCoord = finalSize + xCoord;
}
yCoord = finalSize + yCoord;//The y coordinate of the first pixel in source
}
return scambledImage;
}
rows = 6, and cols = 6;
image size = 648 x 484
this is the exception but don't know how to go about fixing:
java.lang.IllegalArgumentException: y + height must be <= bitmap.height()
Image I'm splitting
Thanks!
Your trying to grab sections of the original bitmap that don't exist.
Put a breakpoint at the line:
scambledImage.add(Bitmap.createBitmap(bMapScaled, xCoord, yCoord, finalSize, finalSize));
And you'll see it fails sometime after the first array iteration because each time your offsetting the start point of which "slice" of the bigmap you are grabbing by xCoord/yCoord.
My hunch says your calculation for finalSize is wrong, but I can only speculate since we don't know exactly what your trying to accomplish.
I have tried your code and changes a little bit and it works for me.
private ArrayList<Bitmap> splitImage(Bitmap bitmap, int rows, int cols){
int chunks = rows*cols;
int chunkHeight,chunkWidth;
ArrayList<Bitmap> splittedImages = null;
splittedImages = new ArrayList<Bitmap>(chunks);
chunkHeight = bitmap.getHeight()/rows;
chunkWidth = bitmap.getWidth()/cols;
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap,bitmap.getWidth(), bitmap.getHeight(), true);
int yCoord = 0;
for(int x=0; x<rows; x++){
int xCoord = 0;
for(int y=0; y<cols; y++){
splittedImages.add(Bitmap.createBitmap(scaledBitmap, xCoord, yCoord, chunkWidth, chunkHeight));
xCoord += chunkWidth;
}
yCoord += chunkHeight;
}
return splittedImages;
}
Related
I want to check the color of pixels in a section of a bitmap. I need to determine at what position the black pixels start. Here is my implementation.
Bitmap bmp = SavePixels(0, 0, screenWidth, screenHeight, gl);
for(int i = 0; i < 100; i++){
for(int x = 0; x < 50; x++){
//Log.i(EJ.TAG, i+","+x);
int pixel = bmp.getPixel(i,x);
if(pixel == Color.BLACK){
cordinate = i;
Log.i(EJ.TAG, i+","+x);
break;
}
}
}
Unfortunately the getPixel method always returns 0
This should find the first column (from left) containing a black pixel:
Bitmap bmp = SavePixels(0, 0, screenWidth, screenHeight, gl);
boolean found = false;
for(int x = 0; x < 50 && !found; x++){
for(int y = 0; y < 100 && !found; y++){
int pixel = bmp.getPixel(x, y);
if(pixel == Color.BLACK){
cordinate = x;
found=true;
}
}
}
First of all sorry for my bad English,i am working on Image Splitter app and it is completed,but now the requirement is that when the image is split(divide into pieces/chunks) then the every piece(chunk) of image block is 50*50 or 40*40,and the most important thing is that for example the original image size is 420*320(it is dynamic and i get the image from gallery),then after split(divide) the image into pieces(chunks) the image size will be still the same 420*320 as i mentioned above,and for example the image size is 420*320 and after split the image and divide every block size equivalent 50*50,then the remaining 20 size will be assign to the last or any block,so i have 2 issue:
Note: In my app i am getting the image into gallery then split the image and shuffle the image pieces(chunks), and then merge the image, and create a **canvas for drawing all those small(chunks) images .**
after splitting the 420*320 size of image,need the image block(chunk) size 50*50 or 40*40.
the remaining 20*20 block(chunk) assign to the last block or any of the other block.
This is the original image before splitting and it's dimension is 420*320:
and this is the image after splitting,the overall image dimension is same 420*320,but the piece(chunk) of image block size is 84*64,and i want to block size is 50*50 or 40*40 and overall image size will also be same 420*320,and the remaining size will be assign to the last block.
This is my Activity:
package com.example.imagesplitter;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
//public class ImageActivity extends Activity implements OnClickListener {
public class ImageActivity extends Activity {
Button split_image;
Button btnGallery;
ImageView image;
Uri selectedImage;
private final int RESULT_LOAD_IMAGE = 1;
int chunkNumbers = 25;
ArrayList<Bitmap> chunkedImages;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
image = (ImageView) findViewById(R.id.source_image);
alertDialogForCameraImage();
}
void pickImageFromGallery() {
Intent pickPhoto = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
// startActivityForResult(pickPhoto , 0);
startActivityForResult(pickPhoto, RESULT_LOAD_IMAGE);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode){
case RESULT_LOAD_IMAGE:
if(resultCode==Activity.RESULT_OK) {
// takenPictureData = handleResultFromChooser(data);
selectedImage = data.getData();
String[] filePathColumn = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(selectedImage,
filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String picturePath = cursor.getString(columnIndex);
cursor.close();
// ImageView imageView = (ImageView) findViewById(R.id.imgView);
image.setImageBitmap(BitmapFactory.decodeFile(picturePath));
// Function of split the image(divide the image into pieces)
splitImage(image, chunkNumbers);
}
break;
}
//And show the result in the image view when take picture from camera.
}
public void alertDialogForCameraImage() {
AlertDialog.Builder adb = new AlertDialog.Builder(ImageActivity.this);
adb.setTitle("Pick Image From Gallery: ");
adb.setNegativeButton("Gallery", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
pickImageFromGallery();
} });
adb.show();
}
/**
* Splits the source image and show them all into a grid in a new activity
*
* #param image The source image to split
* #param chunkNumbers The target number of small image chunks to be formed from the source image
*/
private void splitImage(ImageView image, int chunkNumbers) {
//For the number of rows and columns of the grid to be displayed
int rows,cols;
//For height and width of the small image chunks
int chunkHeight,chunkWidth;
//To store all the small image chunks in bitmap format in this list
chunkedImages = new ArrayList<Bitmap>(chunkNumbers);
//Getting the scaled bitmap of the source image
BitmapDrawable drawable = (BitmapDrawable) image.getDrawable();
Bitmap bitmap = drawable.getBitmap();
/*ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);*/
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight(), true);
rows = cols = (int) Math.sqrt(chunkNumbers);
chunkHeight = bitmap.getHeight()/rows;
chunkWidth = bitmap.getWidth()/cols;
/*chunkHeight = 300/rows;
chunkWidth = 300/cols;*/
//xCoord and yCoord are the pixel positions of the image chunks
int yCoord = 0;
for(int x=0; x<rows; x++){
int xCoord = 0;
for(int y=0; y<cols; y++){
chunkedImages.add(Bitmap.createBitmap(scaledBitmap, xCoord, yCoord, chunkWidth, chunkHeight));
xCoord += chunkWidth;
}
yCoord += chunkHeight;
}
// Function of merge the chunks images(after image divided in pieces then i can call this function to combine and merge the image as one)
mergeImage(chunkedImages);
}
void mergeImage(ArrayList<Bitmap> imageChunks) {
Collections.shuffle(imageChunks);
//Get the width and height of the smaller chunks
int chunkWidth = imageChunks.get(0).getWidth();
int chunkHeight = imageChunks.get(0).getHeight();
//create a bitmap of a size which can hold the complete image after merging
Bitmap bitmap = Bitmap.createBitmap(chunkWidth * 5, chunkHeight * 5, Bitmap.Config.ARGB_4444);
//create a canvas for drawing all those small images
Canvas canvas = new Canvas(bitmap);
int count = 0;
for(int rows = 0; rows < 5; rows++){
for(int cols = 0; cols < 5; cols++){
canvas.drawBitmap(imageChunks.get(count), chunkWidth * cols, chunkHeight * rows, null);
count++;
}
}
/*
* The result image is shown in a new Activity
*/
Intent intent = new Intent(ImageActivity.this, MergedImage.class);
intent.putExtra("merged_image", bitmap);
startActivity(intent);
finish();
}
}
and this is my image split method:
private void splitImage(ImageView image, int chunkNumbers) {
//For the number of rows and columns of the grid to be displayed
int rows,cols;
//For height and width of the small image chunks
int chunkHeight,chunkWidth;
//To store all the small image chunks in bitmap format in this list
chunkedImages = new ArrayList<Bitmap>(chunkNumbers);
//Getting the scaled bitmap of the source image
BitmapDrawable drawable = (BitmapDrawable) image.getDrawable();
Bitmap bitmap = drawable.getBitmap();
/*ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);*/
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight(), true);
rows = cols = (int) Math.sqrt(chunkNumbers);
chunkHeight = bitmap.getHeight()/rows;
chunkWidth = bitmap.getWidth()/cols;
/*chunkHeight = 300/rows;
chunkWidth = 300/cols;*/
//xCoord and yCoord are the pixel positions of the image chunks
int yCoord = 0;
for(int x=0; x<rows; x++){
int xCoord = 0;
for(int y=0; y<cols; y++){
chunkedImages.add(Bitmap.createBitmap(scaledBitmap, xCoord, yCoord, chunkWidth, chunkHeight));
xCoord += chunkWidth;
}
yCoord += chunkHeight;
}
mergeImage(chunkedImages);
}
Any help will be highly appreciated Thanks alot in advance.
EDITED:
Updated:
This is the example image and i want it like this:
Updated:
i think it should be like this:
As I understand the task, if the original image size is 420x320 and block size is 50x50 we will have 7x5 50x50 chunks, 5 70x50 chunks (the last column), 7 50x70 chunks (the last row) and one 70x70 chunk (bottom-right corner).
Then after shuffling we need to put it all together. However the most likely there will be collisions if we just randomly merge chunks (red cross on the picture).
So in that case I determine randomly the position (X,Y) of big square chunk (70x70) and put all 70x50 chunks in the X column and all 50x70 chunks in the Y row.
There might be some other cases:
if the original image size is 200x180 then we will have 4x2 50x50 chunks and 4 50x80 chunks. Then we shuffle it and should put one higher chunk to one column to preserve original image size;
if the original image is 230x200 then we will have 3x4 50x50 chunks and 4 80x50 chunks. Then we should put one wider chunk to one row;
if the original image is 200x200 then your code works perfectly.
As we have chunks with different sizes merge becomes a little more complex - we determine each chunk coordinates depending on the previous chunks size.
package com.example.imagesplitter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
public class MainActivity extends Activity {
Button split_image;
Button btnGallery;
ImageView sourceImage;
Uri selectedImage;
private final int RESULT_LOAD_IMAGE = 1;
int chunkSideLength = 50;
ArrayList<Bitmap> chunkedImage;
// Number of rows and columns in chunked image
int rows, cols;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
sourceImage = (ImageView) findViewById(R.id.source_image);
alertDialogForCameraImage();
}
void pickImageFromGallery() {
Intent pickPhoto = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
// startActivityForResult(pickPhoto , 0);
startActivityForResult(pickPhoto, RESULT_LOAD_IMAGE);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case RESULT_LOAD_IMAGE:
if (resultCode == Activity.RESULT_OK) {
// takenPictureData = handleResultFromChooser(data);
selectedImage = data.getData();
String[] filePathColumn = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null,
null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String picturePath = cursor.getString(columnIndex);
cursor.close();
// ImageView imageView = (ImageView) findViewById(R.id.imgView);
sourceImage.setImageBitmap(BitmapFactory.decodeFile(picturePath));
// Function of split the image(divide the image into pieces)
splitImage(sourceImage, chunkSideLength);
}
break;
}
// And show the result in the image view when take picture from camera.
}
public void alertDialogForCameraImage() {
AlertDialog.Builder adb = new AlertDialog.Builder(MainActivity.this);
adb.setTitle("Pick Image From Gallery: ");
adb.setNegativeButton("Gallery", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
pickImageFromGallery();
}
});
adb.show();
}
/**
* Splits the source image and show them all into a grid in a new activity
*
* #param image
* The source image to split
* #param chunkSideLength
* Image parts side length
*/
private void splitImage(ImageView image, int chunkSideLength) {
Random random = new Random(System.currentTimeMillis());
// height and weight of higher|wider chunks if they would be
int higherChunkSide, widerChunkSide;
// Getting the scaled bitmap of the source image
Bitmap bitmap = ((BitmapDrawable) image.getDrawable()).getBitmap();
rows = bitmap.getHeight() / chunkSideLength;
higherChunkSide = bitmap.getHeight() % chunkSideLength + chunkSideLength;
cols = bitmap.getWidth() / chunkSideLength;
widerChunkSide = bitmap.getWidth() % chunkSideLength + chunkSideLength;
// To store all the small image chunks in bitmap format in this list
chunkedImage = new ArrayList<Bitmap>(rows * cols);
if (higherChunkSide != chunkSideLength) {
if (widerChunkSide != chunkSideLength) {
// picture has both higher and wider chunks plus one big square chunk
ArrayList<Bitmap> widerChunks = new ArrayList<Bitmap>(rows - 1);
ArrayList<Bitmap> higherChunks = new ArrayList<Bitmap>(cols - 1);
Bitmap squareChunk;
int yCoord = 0;
for (int y = 0; y < rows - 1; ++y) {
int xCoord = 0;
for (int x = 0; x < cols - 1; ++x) {
chunkedImage.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, chunkSideLength, chunkSideLength));
xCoord += chunkSideLength;
}
// add last chunk in a row to array of wider chunks
widerChunks.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, widerChunkSide, chunkSideLength));
yCoord += chunkSideLength;
}
// add last row to array of higher chunks
int xCoord = 0;
for (int x = 0; x < cols - 1; ++x) {
higherChunks.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, chunkSideLength, higherChunkSide));
xCoord += chunkSideLength;
}
//save bottom-right big square chunk
squareChunk = Bitmap.createBitmap(bitmap, xCoord, yCoord, widerChunkSide, higherChunkSide);
//shuffle arrays
Collections.shuffle(chunkedImage);
Collections.shuffle(higherChunks);
Collections.shuffle(widerChunks);
//determine random position of big square chunk
int bigChunkX = random.nextInt(cols);
int bigChunkY = random.nextInt(rows);
//add wider and higher chunks into resulting array of chunks
//all wider(higher) chunks should be in one column(row) to avoid collisions between chunks
//We must insert it row by row because they will displace each other from their columns otherwise
for (int y = 0; y < rows - 1; ++y) {
chunkedImage.add(cols * y + bigChunkX, widerChunks.get(y));
}
//And then we insert the whole row of higher chunks
for (int x = 0; x < cols - 1; ++x) {
chunkedImage.add(bigChunkY * cols + x, higherChunks.get(x));
}
chunkedImage.add(bigChunkY * cols + bigChunkX, squareChunk);
} else {
// picture has only number of higher chunks
ArrayList<Bitmap> higherChunks = new ArrayList<Bitmap>(cols);
int yCoord = 0;
for (int y = 0; y < rows - 1; ++y) {
int xCoord = 0;
for (int x = 0; x < cols; ++x) {
chunkedImage.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, chunkSideLength, chunkSideLength));
xCoord += chunkSideLength;
}
yCoord += chunkSideLength;
}
// add last row to array of higher chunks
int xCoord = 0;
for (int x = 0; x < cols; ++x) {
higherChunks.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, chunkSideLength, higherChunkSide));
xCoord += chunkSideLength;
}
//shuffle arrays
Collections.shuffle(chunkedImage);
Collections.shuffle(higherChunks);
//add higher chunks into resulting array of chunks
//Each higher chunk should be in his own column to preserve original image size
//We must insert it row by row because they will displace each other from their columns otherwise
List<Point> higherChunksPositions = new ArrayList<Point>(cols);
for (int x = 0; x < cols; ++x) {
higherChunksPositions.add(new Point(x, random.nextInt(rows)));
}
//sort positions of higher chunks. THe upper-left elements should be first
Collections.sort(higherChunksPositions, new Comparator<Point>() {
#Override
public int compare(Point lhs, Point rhs) {
if (lhs.y != rhs.y) {
return lhs.y < rhs.y ? -1 : 1;
} else if (lhs.x != rhs.x) {
return lhs.x < rhs.x ? -1 : 1;
}
return 0;
}
});
for (int x = 0; x < cols; ++x) {
Point currentCoord = higherChunksPositions.get(x);
chunkedImage.add(currentCoord.y * cols + currentCoord.x, higherChunks.get(x));
}
}
} else {
if (widerChunkSide != chunkSideLength) {
// picture has only number of wider chunks
ArrayList<Bitmap> widerChunks = new ArrayList<Bitmap>(rows);
int yCoord = 0;
for (int y = 0; y < rows; ++y) {
int xCoord = 0;
for (int x = 0; x < cols - 1; ++x) {
chunkedImage.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, chunkSideLength, chunkSideLength));
xCoord += chunkSideLength;
}
// add last chunk in a row to array of wider chunks
widerChunks.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, widerChunkSide, chunkSideLength));
yCoord += chunkSideLength;
}
//shuffle arrays
Collections.shuffle(chunkedImage);
Collections.shuffle(widerChunks);
//add wider chunks into resulting array of chunks
//Each wider chunk should be in his own row to preserve original image size
for (int y = 0; y < rows; ++y) {
chunkedImage.add(cols * y + random.nextInt(cols), widerChunks.get(y));
}
} else {
// picture perfectly splits into square chunks
int yCoord = 0;
for (int y = 0; y < rows; ++y) {
int xCoord = 0;
for (int x = 0; x < cols; ++x) {
chunkedImage.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, chunkSideLength, chunkSideLength));
xCoord += chunkSideLength;
}
yCoord += chunkSideLength;
}
Collections.shuffle(chunkedImage);
}
}
// Function of merge the chunks images(after image divided in pieces then i can call this function to combine
// and merge the image as one)
mergeImage(chunkedImage, bitmap.getWidth(), bitmap.getHeight());
}
void mergeImage(ArrayList<Bitmap> imageChunks, int width, int height) {
// create a bitmap of a size which can hold the complete image after merging
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
// create a canvas for drawing all those small images
Canvas canvas = new Canvas(bitmap);
int count = 0;
Bitmap currentChunk = imageChunks.get(0);
//Array of previous row chunks bottom y coordinates
int[] yCoordinates = new int[cols];
Arrays.fill(yCoordinates, 0);
for (int y = 0; y < rows; ++y) {
int xCoord = 0;
for (int x = 0; x < cols; ++x) {
currentChunk = imageChunks.get(count);
canvas.drawBitmap(currentChunk, xCoord, yCoordinates[x], null);
xCoord += currentChunk.getWidth();
yCoordinates[x] += currentChunk.getHeight();
count++;
}
}
/*
* The result image is shown in a new Activity
*/
Intent intent = new Intent(MainActivity.this, MergedImage.class);
intent.putExtra("merged_image", bitmap);
startActivity(intent);
finish();
}
}
And sorry for my bad English :)
EDITED:
If you want to get the original image, you need to comment all shufflings and place big square chunk in its old place: in the bottom-right corner
//shuffle arrays
/* Collections.shuffle(chunkedImage);
Collections.shuffle(higherChunks);
Collections.shuffle(widerChunks);
*/
//determine random position of big square chunk
int bigChunkX = cols - 1;
int bigChunkY = rows - 1;
And this is only true if both image width and height are not divisible by chunkSideLength.
In other cases you should also comment shufflings and put higher/wider chunks in its old place. The full code of splitImage function with disabled shufflings is below
private void splitImage(ImageView image, int chunkSideLength) {
Random random = new Random(System.currentTimeMillis());
// height and weight of higher|wider chunks if they would be
int higherChunkSide, widerChunkSide;
// Getting the scaled bitmap of the source image
Bitmap bitmap = ((BitmapDrawable) image.getDrawable()).getBitmap();
rows = bitmap.getHeight() / chunkSideLength;
higherChunkSide = bitmap.getHeight() % chunkSideLength + chunkSideLength;
cols = bitmap.getWidth() / chunkSideLength;
widerChunkSide = bitmap.getWidth() % chunkSideLength + chunkSideLength;
// To store all the small image chunks in bitmap format in this list
chunkedImage = new ArrayList<Bitmap>(rows * cols);
if (higherChunkSide != chunkSideLength) {
if (widerChunkSide != chunkSideLength) {
// picture has both higher and wider chunks plus one big square chunk
ArrayList<Bitmap> widerChunks = new ArrayList<Bitmap>(rows - 1);
ArrayList<Bitmap> higherChunks = new ArrayList<Bitmap>(cols - 1);
Bitmap squareChunk;
int yCoord = 0;
for (int y = 0; y < rows - 1; ++y) {
int xCoord = 0;
for (int x = 0; x < cols - 1; ++x) {
chunkedImage.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, chunkSideLength, chunkSideLength));
xCoord += chunkSideLength;
}
// add last chunk in a row to array of wider chunks
widerChunks.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, widerChunkSide, chunkSideLength));
yCoord += chunkSideLength;
}
// add last row to array of higher chunks
int xCoord = 0;
for (int x = 0; x < cols - 1; ++x) {
higherChunks.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, chunkSideLength, higherChunkSide));
xCoord += chunkSideLength;
}
//save bottom-right big square chunk
squareChunk = Bitmap.createBitmap(bitmap, xCoord, yCoord, widerChunkSide, higherChunkSide);
//shuffle arrays
/* Collections.shuffle(chunkedImage);
Collections.shuffle(higherChunks);
Collections.shuffle(widerChunks);
*/
//determine random position of big square chunk
int bigChunkX = cols - 1;
int bigChunkY = rows - 1;
//add wider and higher chunks into resulting array of chunks
//all wider(higher) chunks should be in one column(row) to avoid collisions between chunks
//We must insert it row by row because they will displace each other from their columns otherwise
for (int y = 0; y < rows - 1; ++y) {
chunkedImage.add(cols * y + bigChunkX, widerChunks.get(y));
}
//And then we insert the whole row of higher chunks
for (int x = 0; x < cols - 1; ++x) {
chunkedImage.add(bigChunkY * cols + x, higherChunks.get(x));
}
chunkedImage.add(bigChunkY * cols + bigChunkX, squareChunk);
} else {
// picture has only number of higher chunks
ArrayList<Bitmap> higherChunks = new ArrayList<Bitmap>(cols);
int yCoord = 0;
for (int y = 0; y < rows - 1; ++y) {
int xCoord = 0;
for (int x = 0; x < cols; ++x) {
chunkedImage.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, chunkSideLength, chunkSideLength));
xCoord += chunkSideLength;
}
yCoord += chunkSideLength;
}
// add last row to array of higher chunks
int xCoord = 0;
for (int x = 0; x < cols; ++x) {
higherChunks.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, chunkSideLength, higherChunkSide));
xCoord += chunkSideLength;
}
//shuffle arrays
/* Collections.shuffle(chunkedImage);
Collections.shuffle(higherChunks);
*/
//add higher chunks into resulting array of chunks
//Each higher chunk should be in his own column to preserve original image size
//We must insert it row by row because they will displace each other from their columns otherwise
List<Point> higherChunksPositions = new ArrayList<Point>(cols);
for (int x = 0; x < cols; ++x) {
higherChunksPositions.add(new Point(x, rows - 1));
}
//sort positions of higher chunks. THe upper-left elements should be first
Collections.sort(higherChunksPositions, new Comparator<Point>() {
#Override
public int compare(Point lhs, Point rhs) {
if (lhs.y != rhs.y) {
return lhs.y < rhs.y ? -1 : 1;
} else if (lhs.x != rhs.x) {
return lhs.x < rhs.x ? -1 : 1;
}
return 0;
}
});
for (int x = 0; x < cols; ++x) {
Point currentCoord = higherChunksPositions.get(x);
chunkedImage.add(currentCoord.y * cols + currentCoord.x, higherChunks.get(x));
}
}
} else {
if (widerChunkSide != chunkSideLength) {
// picture has only number of wider chunks
ArrayList<Bitmap> widerChunks = new ArrayList<Bitmap>(rows);
int yCoord = 0;
for (int y = 0; y < rows; ++y) {
int xCoord = 0;
for (int x = 0; x < cols - 1; ++x) {
chunkedImage.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, chunkSideLength, chunkSideLength));
xCoord += chunkSideLength;
}
// add last chunk in a row to array of wider chunks
widerChunks.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, widerChunkSide, chunkSideLength));
yCoord += chunkSideLength;
}
//shuffle arrays
/* Collections.shuffle(chunkedImage);
Collections.shuffle(widerChunks);
*/
//add wider chunks into resulting array of chunks
//Each wider chunk should be in his own row to preserve original image size
for (int y = 0; y < rows; ++y) {
chunkedImage.add(cols * y + cols - 1, widerChunks.get(y));
}
} else {
// picture perfectly splits into square chunks
int yCoord = 0;
for (int y = 0; y < rows; ++y) {
int xCoord = 0;
for (int x = 0; x < cols; ++x) {
chunkedImage.add(Bitmap.createBitmap(bitmap, xCoord, yCoord, chunkSideLength, chunkSideLength));
xCoord += chunkSideLength;
}
yCoord += chunkSideLength;
}
/* Collections.shuffle(chunkedImage);
*/ }
}
// Function of merge the chunks images(after image divided in pieces then i can call this function to combine
// and merge the image as one)
mergeImage(chunkedImage, bitmap.getWidth(), bitmap.getHeight());
}
I've searched for how to pixelate an image in android via code, the results are varied.
I've found libraries and tutorials on how to apply other effects found here: http://xjaphx.wordpress.com/learning/tutorials/
Can someone clear things up for me, what is the simplest way of pixelating an image on the fly in android
Also it would be handy if it was a function that I could how many rounds or how much I wanted the image pixelating.
Thank in advance.
The simplest way to pixelate the image would be to scale image down using "nearest neighbour" algorithm, and then scale up, using the same algorithm.
Filtering over the image trying to find an average takes much more time, but does not actually give any improvements in result quality, after all you do intentionally want your image distorted.
I have done this before in vb.net and its easily made into a function whose parameter can control how pixelated you want it.
The basic idea is to scan the image in section of blocks of X width and y height. for each block you find the average RGB value and set all those pixels to that color. the smaller the block size the less pixelated.
int avR,avB,avG; // store average of rgb
int pixel;
Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
for(int x = 0; x < width; x+= pixelationAmount) { // do the whole image
for(int y = 0; y < height; y++ pixelationamount) {
avR = 0; avG = 0; avB =0;
for(int xx =x; xx <pixelationAmount;xx++){// YOU WILL WANT TO PUYT SOME OUT OF BOUNDS CHECKING HERE
for(int yy= y; yy <pixelationAmount;yy++){ // this is scanning the colors
pixel = src.getPixel(x, y);
avR += (int) (color.red(pixel);
avG+= (int) (color.green(pixel);
avB += (int) (color.blue(pixel);
}
}
avrR/= pixelationAmount^2; //divide all by the amount of samples taken to get an average
avrG/= pixelationAmount^2;
avrB/= pixelationAmount^2;
for(int xx =x; xx <pixelationAmount;xx++){// YOU WILL WANT TO PUYT SOME OUT OF BOUNDS CHECKING HERE
for(int yy= y; yy <pixelationAmount;yy++){ // this is going back over the block
bmOut.setPixel(xx, yy, Color.argb(255, avR, avG,avB)); //sets the block to the average color
}
}
}
}
sorry about the bad formatting (wrote it in notepad quickly) but thought it might give you a framework to make your own pixelate function
This is corrected of above algorithm that works:
Bitmap bmOut = Bitmap.createBitmap(OriginalBitmap.getWidth(),OriginalBitmap.getHeight(),OriginalBitmap.getConfig());
int pixelationAmount = 50; //you can change it!!
int width = OriginalBitmap.getWidth();
int height = OriginalBitmap.getHeight();
int avR,avB,avG; // store average of rgb
int pixel;
for(int x = 0; x < width; x+= pixelationAmount) { // do the whole image
for(int y = 0; y < height; y+= pixelationAmount) {
avR = 0; avG = 0; avB =0;
int bx = x + pixelationAmount;
int by = y + pixelationAmount;
if(by >= height) by = height;
if(bx >= width)bx = width;
for(int xx =x; xx < bx;xx++){// YOU WILL WANT TO PUYT SOME OUT OF BOUNDS CHECKING HERE
for(int yy= y; yy < by;yy++){ // this is scanning the colors
pixel = OriginalBitmap.getPixel(xx, yy);
avR += (int) (Color.red(pixel));
avG+= (int) (Color.green(pixel));
avB += (int) (Color.blue(pixel));
}
}
avR/= pixelationAmount^2; //divide all by the amount of samples taken to get an average
avG/= pixelationAmount^2;
avB/= pixelationAmount^2;
for(int xx =x; xx < bx;xx++)// YOU WILL WANT TO PUYT SOME OUT OF BOUNDS CHECKING HERE
for(int yy= y; yy <by;yy++){ // this is going back over the block
bmOut.setPixel(xx, yy, Color.argb(255, avR, avG,avB)); //sets the block to the average color
}
}
}
iv.setImageBitmap(bmOut);
anyway it was not what i was looking for
I have change previous algorithm completely and it really done something like mosaic filter!
the idea is to replace each block pixels with its below block pixels
use this function simply:
public void filter(){
Bitmap bmOut = Bitmap.createBitmap(OriginalBitmap.getWidth(),OriginalBitmap.getHeight(),OriginalBitmap.getConfig());
int pixelationAmount = 10;
Bitmap a = Bitmap.createBitmap(pixelationAmount,pixelationAmount,OriginalBitmap.getConfig());
Bitmap b = Bitmap.createBitmap(pixelationAmount,pixelationAmount,OriginalBitmap.getConfig());
int width = OriginalBitmap.getWidth();
int height = OriginalBitmap.getHeight();
int pixel;
int counter = 1;
int px = 0;int py = 0;int pbx=0;int pby=0;
for(int x = 0; x < width; x+= pixelationAmount) { // do the whole image
for(int y = 0; y < height; y+= pixelationAmount) {
int bx = x + pixelationAmount;
int by = y + pixelationAmount;
if(by >= height) by = height;
if(bx >= width)bx = width;
int xxx = -1;
int yyy = -1;
for(int xx =x; xx < bx;xx++){// YOU WILL WANT TO PUYT SOME OUT OF BOUNDS CHECKING HERE
xxx++;
yyy = -1;
for(int yy= y; yy < by;yy++){ // this is scanning the colors
yyy++;
pixel = OriginalBitmap.getPixel(xx, yy);
if(counter == 1)
{
a.setPixel(xxx, yyy, pixel);
px = x;//previous x
py = y;//previous y
pbx = bx;
pby = by;
}
else
b.setPixel(xxx, yyy, pixel);
}
}
counter++;
if(counter == 3)
{
int xxxx = -1;
int yyyy = -1;
for(int xx =x; xx < bx;xx++)
{
xxxx++;
yyyy = -1;
for(int yy= y; yy <by;yy++){
yyyy++;
bmOut.setPixel(xx, yy, b.getPixel(xxxx, yyyy));
}
}
for(int xx =px; xx < pbx;xx++)
{
for(int yy= py; yy <pby;yy++){
bmOut.setPixel(xx, yy, a.getPixel(xxxx, yyyy)); //sets the block to the average color
}
}
counter = 1;
}
}
}
image_view.setImageBitmap(bmOut);
}
This is the code I used:
ImageFilter is the parent class:
public abstract class ImageFilter {
protected int [] pixels;
protected int width;
protected int height;
public ImageFilter (int [] _pixels, int _width,int _height){
setPixels(_pixels,_width,_height);
}
public void setPixels(int [] _pixels, int _width,int _height){
pixels = _pixels;
width = _width;
height = _height;
}
/**
* a weighted Euclidean distance in RGB space
* #param c1
* #param c2
* #return
*/
public double colorDistance(int c1, int c2)
{
int red1 = Color.red(c1);
int red2 = Color.red(c2);
int rmean = (red1 + red2) >> 1;
int r = red1 - red2;
int g = Color.green(c1) - Color.green(c2);
int b = Color.blue(c1) - Color.blue(c2);
return Math.sqrt((((512+rmean)*r*r)>>8) + 4*g*g + (((767-rmean)*b*b)>>8));
}
public abstract int[] procImage();
}
public class PixelateFilter extends ImageFilter {
int pixelSize;
int[] colors;
/**
* #param _pixels
* #param _width
* #param _height
*/
public PixelateFilter(int[] _pixels, int _width, int _height) {
this(_pixels, _width, _height, 10);
}
public PixelateFilter(int[] _pixels, int _width, int _height, int _pixelSize) {
this(_pixels, _width, _height, _pixelSize, null);
}
public PixelateFilter(int[] _pixels, int _width, int _height, int _pixelSize, int[] _colors) {
super(_pixels, _width, _height);
pixelSize = _pixelSize;
colors = _colors;
}
/* (non-Javadoc)
* #see imageProcessing.ImageFilter#procImage()
*/
#Override
public int[] procImage() {
for (int i = 0; i < width; i += pixelSize) {
for (int j = 0; j < height; j += pixelSize) {
int rectColor = getRectColor(i, j);
fillRectColor(rectColor, i, j);
}
}
return pixels;
}
private int getRectColor(int col, int row) {
int r = 0, g = 0, b = 0;
int sum = 0;
for (int x = col; x < col + pixelSize; x++) {
for (int y = row; y < row + pixelSize; y++) {
int index = x + y * width;
if (index < width * height) {
int color = pixels[x + y * width];
r += Color.red(color);
g += Color.green(color);
b += Color.blue(color);
}
}
}
sum = pixelSize * pixelSize;
int newColor = Color.rgb(r / sum, g / sum, b / sum);
if (colors != null)
newColor = getBestMatch(newColor);
return newColor;
}
private int getBestMatch(int color) {
double diff = Double.MAX_VALUE;
int res = color;
for (int c : colors) {
double currDiff = colorDistance(color, c);
if (currDiff < diff) {
diff = currDiff;
res = c;
}
}
return res;
}
private void fillRectColor(int color, int col, int row) {
for (int x = col; x < col + pixelSize; x++) {
for (int y = row; y < row + pixelSize; y++) {
int index = x + y * width;
if (x < width && y < height && index < width * height) {
pixels[x + y * width] = color;
}
}
}
}
public static final Bitmap changeToPixelate(Bitmap bitmap, int pixelSize, int [] colors) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
PixelateFilter pixelateFilter = new PixelateFilter(pixels, width, height, pixelSize, colors);
int[] returnPixels = pixelateFilter.procImage();
Bitmap returnBitmap = Bitmap.createBitmap(returnPixels, width, height, Bitmap.Config.ARGB_8888);
return returnBitmap;
}
}
Here is how you use it:
int [] colors = new int [] { Color.BLACK,Color.WHITE,Color.BLUE,Color.CYAN,Color.RED};
final Bitmap bmOut = PixelateFilter.changeToPixelate(OriginalBitmap, pixelSize,colors);
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);
protected IplImage processImage(byte[] data, int width, int height) {
int f = 4;// SUBSAMPLING_FACTOR;
// First, downsample our image and convert it into a grayscale IplImage
IplImage grayImage = IplImage.create(width / f, height / f, IPL_DEPTH_8U, 1);
int imageWidth = grayImage.width();
int imageHeight = grayImage.height();
int dataStride = f * width;
int imageStride = grayImage.widthStep();
ByteBuffer imageBuffer = grayImage.getByteBuffer();
for (int y = 0; y < imageHeight; y++) {
int dataLine = y * dataStride;
int imageLine = y * imageStride;
for (int x = 0; x < imageWidth; x++) {
imageBuffer.put(imageLine + x, data[dataLine + f * x]);
}
}
return grayImage;
}
I should get an IplImage from onPreviewFrame(byte[] data, Camera camera).
In the way i wrote up there i get an iplimage much smaller than the original one.
There is any way to keep the same dimension on the new iplimage? Whould i avoid the subsampling factor? how can i do that?
Yes just put set f = 1 and it won't subsample.