I am using the following code to blur an image in android, but it does not work. The final image that I get is with very distorted color and not a blur which I want. What am I doing wrong?
public Bitmap blurBitmap(Bitmap bmpOriginal)
{
int width, height;
height = bmpOriginal.getHeight();
width = bmpOriginal.getWidth();
Bitmap bmpBlurred = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
for (int i = 1; i < width - 1; i++) {
for (int j = 1; j < height - 1; j++) {
int color = (int) getAverageOfPixel(bmpOriginal, i, j);
bmpBlurred.setPixel(i, j, Color.argb(Color.alpha(color), Color.red(color), Color.green(color), Color.blue(color)));
}
}
return bmpBlurred;
}
private double getAverageOfPixel(Bitmap bitmap, int i, int j) {
return (
bitmap.getPixel(i-1, j-1) + bitmap.getPixel(i-1, j) + bitmap.getPixel(i-1, j+1) +
bitmap.getPixel(i, j-1) + bitmap.getPixel(i, j) + bitmap.getPixel(i, j+1) +
bitmap.getPixel(i+1, j-1) + bitmap.getPixel(i+1, j) + bitmap.getPixel(i+1, j+1)) / 9;
}
Your problem is that you are combining all the colour channels at once and they all spill into each other. You should apply your blur function to the red, green, and blue channels separately.
Might it be easier to create a SurfaceView and use the FX_SURFACE_BLUR flag?
Related
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 :)
I already have a cv::Mat in Android NDK
cv::Mat grayMat;
grayMat.create(480, 720, CV_8UC1);
and a TextureView in JAVA.
public class MainActivity extends Activity implements TextureView.SurfaceTextureListener{
private TextureView mTextureView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTextureView = new TextureView(this);
mTextureView.setSurfaceTextureListener(this);
}
....
}
Why: I'm doing some image processing using OpenCV in NDK. Now, I'm using the method that save the result to .jpg and show on ImageView. But next step I'll use camera preview to do some processing. I don't want to save each frames to see the results.
Goal: Show the cv::Mat on Android
Question: How to show the cv::Mat using OpenGL ES 2.0 in NDK??
If you want to display the Mat in Android, you can translate the Mat to pixels, then pass the pixels to Android. In Android, you can translate the pixels to bitmap to display.
here is the code to get the pixels in Mat.
int* getPixel(Mat src){
int w = src.cols;
int h = src.rows;
int* result = new int[w*h];
int channel = src.channels();
if(channel == 1){
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
int value = (int)(*(src.data + src.step[0] * j + src.step[1] * i));
result[j*w+i] = rgb(value, value, value);
}
}
}else if(channel == 3 || channel == 4){
for (int i = 0; i < w; i++){
for (int j = 0; j < h; j++){
int r = (int)(*(src.data + src.step[0] * j + src.step[1] * i + src.elemSize1() * 2));
int g = (int)(*(src.data + src.step[0] * j + src.step[1] * i + src.elemSize1()));
int b = (int)(*(src.data + src.step[0] * j + src.step[1] * i));
result[j*w+i] = rgb(r, g, b);
}
}
}
return result;
}
int rgb(int red, int green, int blue) {
return (0xFF << 24) | (red << 16) | (green << 8) | blue;
}
get bitmap with pixels(also need width and height).
Bitmap bmpReturn = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
bmpReturn.setPixels(matPixels, 0, width, 0, 0, width, height);
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 want to display the negative of a dicom image , by using bitmap I did so
public static Bitmap getNegativeImage(Bitmap img) {
int w1 = img.getWidth();
int h1 = img.getHeight();
// int value[][] = new int[w1][h1];
Bitmap gray = Bitmap.createBitmap(
w1, h1, img.getConfig());
int value, alpha, r, g, b;
for (int i = 0; i < w1; i++) {
for (int j = 0; j < h1; j++) {
value = img.getPixel(i, j); // store value
alpha = getAlpha(value);
r = 255 - getRed(value);
g = 255 - getGreen(value);
b = 255 - getBlue(value);
value = createRGB(alpha, r, g, b);
gray.setPixel(i, j, value);
}
}
return gray;
}
public static int createRGB(int alpha, int r, int g, int b) {
int rgb = (alpha << 24) + (r << 16) + (g << 8) + b;
return rgb;
}
public static int getAlpha(int rgb) {
return (rgb >> 24) & 0xFF;
}
public static int getRed(int rgb) {
return (rgb >> 16) & 0xFF;
}
public static int getGreen(int rgb) {
return (rgb >> 8) & 0xFF;
}
public static int getBlue(int rgb) {
return rgb & 0xFF;
}
But the inverted(negative ) image gets black out, on clicking again the original image appears but the inverted image is not displayed.
Regards
Sathya U M
I don't see why your code won't work, but this should:
private static final int RGB_MASK = 0x00FFFFFF;
public Bitmap invert(Bitmap original) {
// Create mutable Bitmap to invert, argument true makes it mutable
Bitmap inversion = original.copy(Config.ARGB_8888, true);
// Get info about Bitmap
int width = inversion.getWidth();
int height = inversion.getHeight();
int pixels = width * height;
// Get original pixels
int[] pixel = new int[pixels];
inversion.getPixels(pixel, 0, width, 0, 0, width, height);
// Modify pixels
for (int i = 0; i < pixels; i++)
pixel[i] ^= RGB_MASK;
inversion.setPixels(pixel, 0, width, 0, 0, width, height);
// Return inverted Bitmap
return inversion;
}
It creates a mutable copy of the Bitmap (not all Bitmaps are), inverts the rgb part of all pixels, leaving alpha intact
Edit
I have an idea of why your code isn't working: you're assuming that the pixels are AARRGGBB
I have a plain ImageView object colorSquare.
It is straightforward to set the color of the object by calling.
colorSquare.setBackgroundColor(color);
But how do I do the reverse, i.e. retrieve the color of the ImageView background?
What u can do is
get ColorDrawable from ImageView.
ColorDrawable drawable = (ColorDrawable) colorSquare.getBackground();
now
drawable.getColor()
will give u the Color.
This will work only if u have set the Color or else u will get ClassCastException
private int colorfindcolor(View v, int x, int y) {
int offset = 1; // 3x3 Matrix
int pixelsNumber = 0;
int xImage = 0;
int yImage = 0;
Bitmap imageBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.palette_color);
xImage = (int) (x * ((double) imageBitmap.getWidth() / (double) paletteImg
.getWidth()));
yImage = (int) (y * ((double) imageBitmap.getHeight() / (double) paletteImg
.getHeight()));
for (int i = xImage - offset; i <= xImage + offset; i++) {
for (int j = yImage - offset; j <= yImage + offset; j++) {
try {
color = imageBitmap.getPixel(i, j);
pixelsNumber += 1;
} catch (Exception e) {
// Log.w(TAG, "Error picking color!");
}
}
}
return color;
}
Where x,y are event.getX(),event.getX() of imageview touch event