Can someone suggest me a fast method or a library for color splash effect? For example, I select a color and a photo gets desaturated for all colors except that one I have picked.
I have tried using pixel by pixel color check and then replace the color but it is too slow for big images.
int width = originalImage.getWidth();
int height = originalImage.getHeight();
int[] pixels = new int[width * height];
originalImage.getPixels(pixels, 0, width, 0, 0, width, height);
for (int x = 0; x < pixels.length; ++x) {
pixels[x] = Distance(pixels[x], fromColor) < 4 ? targetColor : pixels[x];
}
Bitmap newImage = Bitmap.createBitmap(width, height, originalImage.getConfig());
newImage.setPixels(pixels, 0, width, 0, 0, width, height);
public int Distance(int a, int b) {
return Math.abs(Color.red(a) - Color.red(b)) + Math.abs(Color.green(a) -
Color.green(b)) + Math.abs(Color.blue(a) - Color.blue(b));
}
EDIT:
Here are the original and the processed images, the color I am keeping is #ff9350:
You can try to go over possible color RGB values and prepareHashSet containing only those color values that should not be
desaturated. It seems that there should not be a lot of values.
After that you will be able to check whether the color should be desaturated or not. Thus, you will get rather long precalculation for a new given color, but your code that converts photo will become faster.
It can be implemented like so (haven't tested it under Android, just some kind of Java pseudo-code to show the idea):
// precalculation
Set<Color> keptColors = new HashSet<Color>();
final int MAX_DISTANCE = 4;
for (int r=red(fromColor)-MAX_DISTANCE; r<=red(fromColor)+MAX_DISTANCE; r++) {
for (int g=green(fromColor)-MAX_DISTANCE; g<=green(fromColor)+MAX_DISTANCE; g++) {
for (int b=blue(fromColor)-MAX_DISTANCE; b<=blue(fromColor)+MAX_DISTANCE; b++) {
if (Distance(rgb(r,g,b)), fromColor) < MAX_DISTANCE {
keptColors.add(rgb(r,g,b));
}
}
}
}
...
// in you photo processing code
keptColors.contains(pixels[x]) ? pixels[x] : targetColor;
use inRange function in OpenCV to isolate a Color
Scalar lower(0,100,100);
Scalar upper(10,255,255);
Core.inRange(hsv, lower, upper, segmentedImage);
check out this Answer
Related
Well, I am doing a project with the QR Code. The idea of my project is to combine the numbers of QR Code(8-numbers of QR Code for my project) and generate a single color QR Code which idea is to increase the data storage of the QR Code.
However, so far I have done most of the part but I left the most important part which is generating color QR Code. The color QR Code has to be generated with the hexadecimal color and set the color to every pixel of the QR Code so it would be like a color QR Code. For now, I would try to generate with the Red color 1st.
So I have store the binary value in an ArrayList and the data is some sort like 10101010. Then I change it to hexadecimal. Here is my code:
ArrayList<String>arrayList = new ArrayList<>();
arrayList.add(a1+a2+a3+a4+a5+a6+a7+a8); // Store 1110001 into ArrayList
String [] hexArray = new String[arrayList.size()];
arrayList.toArray(hexArray);
for(int a = 0; a < hexArray.length; a++){
int dec = Integer.parseInt(String.valueOf(arrayList.get(a)),2);
String hexString = Integer.toString(dec, 16);
String alpha = "0xff";
String behind = "0000";
hexArray[a] = alpha+hexString+behind;
}
I have written some code of changing the color, but the code is changing the whole color of the QR Code which is the foreground and the background of QR Code as well.
private Bitmap encode(String contents, int width, int height, #ColorInt int foreground, #ColorInt int background) {
MultiFormatWriter writer = new MultiFormatWriter();
BitMatrix matrix = null;
Bitmap bitmap = null;
try {
matrix = writer.encode(contents, BarcodeFormat.QR_CODE, width, height);
} catch (WriterException e) {
e.printStackTrace();
}
if(matrix != null) {
int[] pixels = new int[width * height];
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = matrix.get(x, y) ? foreground : background;
}
}
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
}
return bitmap;
}
Since ARGB having Alpha, Red, Green, Blue colors. So what if I want to set the hexadecimal value just to Red color only. Then set to every pixel of the QR Code which will become a red color (would slightly different in kind of red color because the hexadecimal value is different) QR Code.
If you are using Color.parseColor(hexadecimal) to generate the color. The hexadecimal should be like #ff00ff RGB format or #ff00ff00 ARGB format.
for(int a = 0; a < hexArrayRed.length; a++){
int dec = Integer.parseInt(String.valueOf(arrayList.get(a)),2);
String hexString = Integer.toString(dec, 16);
while(hexString.length() < 2){
hexString = "0"+hexString;
}
String head = "#ff";
String behind = "0000";
hexArrayRed[a] = head+hexString+behind;
/*
Red Hexadecimal Value --> #ff _ _ 0000
*/
}
For your information, I add while statement to ensure the length of hexString. If the binary is 00000011 and it is 3 in hexadecimal instead of 03. So I add "0" to the hexString.
I would like to change my textviews color when the wallpaper chosen by the user is too light or too dark acting basically as the latest launchers that for instance if it is set a white wallpaper, change all the textviews to a dark color but I dont know how to detect that.
Help. Thanks in advance.
This calculates the (estimated) brightness of a bitmap image.
The argument "skipPixel" defines how many pixels to skip for the brightness calculation, because it might be very runtime intensive to calculate brightness for every single pixel. Higher values result in better performance, but a more estimated result value.
When skipPixel equals 1, the method actually calculates the real average brightness, not an estimated one.
So "skipPixel" needs to be 1 or bigger !
The function returns a brightness level between 0 and 255, where 0 = totally black and 255 = totally bright.
So you have to choose for your own, what "bright" or "dark" means to you.
public int calculateBrightness(android.graphics.Bitmap bitmap, int skipPixel) {
int R = 0; int G = 0; int B = 0;
int height = bitmap.getHeight();
int width = bitmap.getWidth();
int n = 0;
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
for (int i = 0; i < pixels.length; i += skipPixel) {
int color = pixels[i];
R += Color.red(color);
G += Color.green(color);
B += Color.blue(color);
n++;
}
return (R + B + G) / (n * 3);
}
In order to get the Bitmap (Image) from your device, you can use this code:
final String photoPath = "path to your photo"; // Add photo path here
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeFile(photoPath, options);
What I would suggest is that you take the average of the r, g and b value for each pixel (creates a monochrome picture) and then average all pixels to get one global average. That average will be between 0 and 255 (if the image is 8 bits deep). Then, choose a threshold above which the image will be considered light/dark.
I've been searching the internet for changing colour or overlaying a specific part of a bitmap. I've a square bitmap and I want to change the colour in a matrix pattern that is in equal 9 blocks which can be understood from the following image. (Cyan colour line here is for demonstration only)
I've read about boundary fill algorithm in College but here for java, I came to know that it is too bulky to perform such an operation for 9 specific parts. And I don't know how to use Paint with Canvas for such a square scenario.
So is there a method or something which can help me figure out how to paint a specific square by providing the size or location without performing a huge task on UI.
Here's what I need to achieve:
I can change the color, location,size by myself if there's something which can help me out.
Also, as there is white background, is there a way to not paint the background or do I have to use PNG?
Update:
I'm successfully able to divide the image in 9 equal parts using following code but PorterDuffColorFilter is not working as expected.
Code:
public void splitBitmap(Bitmap bitmap) {
int width, height;
// Divide the original bitmap width by the desired vertical column count
width = bitmap.getWidth() / 3;
// Divide the original bitmap height by the desired horizontal row count
height = bitmap.getHeight() / 3;
// Loop the array and create bitmaps for each coordinate
for (int x = 0; x < 3; ++x) {
for (int y = 0; y < 3; ++y) {
// Create the sliced bitmap
smallimages.add(Bitmap.createBitmap(bitmap, x * width, y * height, width, height));
}
}
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
The above code provides 9 bitmaps which then set to a GridLayout. But No mode of PorterDuffColorFilter is useful. Either the images remain original or is painted completely. I've tried every one of the modes available and none worked.
I've done something similar to this so after changing my code a bit, I think this is what you want:
Assuming that you don't have PNG,
First, remove the white background from your image Source:
Setting the white color as Transparent, you can use any color you want.
private static final int[] FROM_COLOR = new int[]{255, 255, 255};
private static final int THRESHOLD = 3;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.test_colors);
ImageView iv = (ImageView) findViewById(R.id.img);
Drawable d = getResources().getDrawable(RES);
iv.setImageDrawable(adjust(d));
}
private Drawable adjust(Drawable d)
{
int to = Color.TRANSPARENT;
//Need to copy to ensure that the bitmap is mutable.
Bitmap src = ((BitmapDrawable) d).getBitmap();
Bitmap bitmap = src.copy(Bitmap.Config.ARGB_8888, true);
for(int x = 0;x < bitmap.getWidth();x++)
for(int y = 0;y < bitmap.getHeight();y++)
if(match(bitmap.getPixel(x, y)))
bitmap.setPixel(x, y, to);
return new BitmapDrawable(bitmap);
}
private boolean match(int pixel)
{
//There may be a better way to match, but I wanted to do a comparison ignoring
//transparency, so I couldn't just do a direct integer compare.
return Math.abs(Color.red(pixel) - FROM_COLOR[0]) < THRESHOLD &&
Math.abs(Color.green(pixel) - FROM_COLOR[1]) < THRESHOLD &&
Math.abs(Color.blue(pixel) - FROM_COLOR[2]) < THRESHOLD;
}
Above code will change the color to transparent and below code will split the bitmap into 9 same size bitmaps:
public void splitBitmap(Bitmap bitmap) {
ArrayList<Bitmap> smallimages = new ArrayList<>(9);
int width, height;
// Divide the original bitmap width by the desired vertical column count
width = bitmap.getWidth() / 3;
// Divide the original bitmap height by the desired horizontal row count
height = bitmap.getHeight() / 3;
// Loop the array and create bitmaps for each coordinate
for (int x = 0; x < 3; ++x) {
for (int y = 0; y < 3; ++y) {
// Create the sliced bitmap
smallimages.add(Bitmap.createBitmap(bitmap, x * width, y * height, width, height));
}
}
}
At last, you can use PorterDuffColorFilter on every bitmap:
imageView.setImageDrawable(arrayList.get(0));
imageView.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_ATOP);
There can be problems as it works for me and might not for you but this is the way you can achieve your needed result.
If any problem persists, I can help.
EDIT: Solved! See below.
I need to crop my image (YUV422888 color space) which I obtain from the onImageAvailable listener of Camera2. I don't want or need to convert it to Bitmap as it affects performance a lot, and also I'm actually interested in luma and not in RGB information (which is contained in Plane 0 of the Image).
I came up with the following solution:
Get the Y' information contained in the Plane 0 of the Image object made available by Camera2 in the listener.
Convert the Y' Plane into a byte[] array in.
Convert the byte[] array to a 2d byte[][] array in order to crop.
Use some for loops to crop at desired left, right, top and bottom coordinates.
Fold the 2d byte[][] array back to a 1d byte[] array out, containing cropped luma Y' information.
Point 4 unfortunately yields a corrupt image. What am I doing wrong?
In the onImageAvailableListener of Camera2 (please note that although I am computing a bitmap, it's only to see what's happening, as I'm not interested in the Bitmap/RGB data):
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer(); // Grab just the Y' Plane.
buffer.rewind();
byte[] data = new byte[buffer.capacity()];
buffer.get(data);
Bitmap bitmap = cropByteArray(data, image.getWidth(), image.getHeight()); // Just for preview/sanity check purposes. The bitmap is **corrupt**.
runOnUiThread(new bitmapRunnable(bitmap) {
#Override
public void run() {
image_view_preview.setImageBitmap(this.bitmap);
}
});
The cropByteArray function needs fixing. It outputs a bitmap that is corrupt, and should output an out byte[] array similar to in, but containing only the cropped area:
public Bitmap cropByteArray(byte[] in, int inw, int inh) {
int l = 100; // left crop start
int r = 400; // right crop end
int t = 400; // top crop start
int b = 700; // top crop end
int outw = r-l;
int outh = b-t;
byte[][] in2d = new byte[inw][inh]; // input width and height are 1080 x 1920.
byte[] out = new byte[outw*outh];
int[] pixels = new int[outw*outh];
i = 0;
for(int col = 0; col < inw; col++) {
for(int row = 0; row < inh; row++) {
in2d[col][row] = in[i++];
}
}
i = 0;
for(int col = l; col < r; col++) {
for(int row = t; row < b; row++) {
//out[i++] = in2d[col][row]; // out is the desired output of the function, but for now we output a bitmap instead
int grey = in2d[col][row] & 0xff;
pixels[i++] = 0xFF000000 | (grey * 0x00010101);
}
}
return Bitmap.createBitmap(pixels, inw, inh, Bitmap.Config.ARGB_8888);
}
EDIT Solved thanks to the suggestion by Eddy Talvala. The following code will yield the Y' (luma plane 0 from ImageReader) cropped to the desired coordinates. The cropped data is in the out byte array. The bitmap is generated just for confirmation. I am also attaching the handy YUVtoGrayscale() function below.
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
int stride = planes[0].getRowStride();
buffer.rewind();
byte[] Y = new byte[buffer.capacity()];
buffer.get(Y);
int t=200; int l=600;
int out_h = 600; int out_w = 600;
byte[] out = new byte[out_w*out_h];
int firstRowOffset = stride * t + l;
for (int row = 0; row < out_h; row++) {
buffer.position(firstRowOffset + row * stride);
buffer.get(out, row * out_w, out_w);
}
Bitmap bitmap = YUVtoGrayscale(out, out_w, out_h);
Here goes the YUVtoGrayscale().
public Bitmap YUVtoGrayscale(byte[] yuv, int width, int height) {
int[] pixels = new int[yuv.length];
for (int i = 0; i < yuv.length; i++) {
int grey = yuv[i] & 0xff;
pixels[i] = 0xFF000000 | (grey * 0x00010101);
}
return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
}
There are some remaining issues. I am using the front camera and although the preview orientation is correct inside the TextureView, the image returned by ImageViewer is rotated clockwise and flipped vertically (a person is lying on their right cheek in the preview, only the right cheek is the left cheek because of the vertical flip) on my device which has sensor orientation of 270 deg. Is there an accepted solution to have both the preview and saved photos in the same, correct orientation using Camera2?
Cheers.
It'd be helpful if you described how the image is corrupt - do you see a valid image but it's distorted, or is it just total garbage, or just total black?
But I'm guessing you're not paying attention to the row stride of the Y plane (https://developer.android.com/reference/android/media/Image.Plane.html#getRowStride() ), which would typically result in an image that's skewed (vertical lines become angled lines).
When accessing the Y plane, the byte index of pixel (x,y) is:
y * rowStride + x
not
y * width + x
because row stride may be larger than width.
I'd also avoid copying so much; you really don't need the 2D array, and a large byte[] for the image also wastes memory.
You can instead seek() to the start of each output row, and then only read the bytes you need to copy straight into your destination byte[] out with ByteBuffer.get(byte[], offset, length).
That'd look something like
int stride = planes[0].getRowStride();
ByteBuffer img = planes[0].getBuffer();
int firstRowOffset = stride * t + l;
for (int row = 0; row < outh; row++) {
img.position(firstRowOffset + row * stride);
img.get(out, row * outw, outw);
}
I have an Android application to display an image on another image, such that second image's white colour is transparent. To do this, I have used two ImageViews, with the original image to be overlaid as bitmap1 and the image to be made transparent as bitmap2. When I run this, I get some exceptions at the setPixel method.
Here's my code:
Bitmap bitmap2 = null;
int width = imViewOverLay.getWidth();
int height = imViewOverLay.getHeight();
for(int x = 0; x < width; x++)
{
for(int y = 0; y < height; y++)
{
if(bitMap1.getPixel(x, y) == Color.WHITE)
{
bitmap2.setPixel(x, y, Color.TRANSPARENT);
}
else
{
bitmap2.setPixel(x, y, bitMap1.getPixel(x, y));
}
}
}
imViewOverLay is the ImageView of the overlay image. Any idea what might be going wrong in the above code?
The most obvious error is that you're not creating bitmap2 - unless you've not posted all the code of course.
You declare it and set it to null, but then don't do anything else until you try to call bitmap2.setPixel.
i think you need to make it mutable
Loading a resource to a mutable bitmap
i did this
BitmapFactory.Options bitopt=new BitmapFactory.Options();
bitopt.inMutable=true;
mSnareBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.snare, bitopt);
also, i found i needed to set alpha to something less than 255 for it to render the image with transparent background.
mPaint.setAlpha(250);
canvas.drawBitmap(mSnareBitmap, 0, 30, mPaint);
by the way, using white as your transparent color isn't a great idea because you will get aliasing problems at the edges of your opaque objects. i use green because my overlay images don't have any green in (like a green screen in the movies) then i can remove the green inside the loop and set the alpha value based on the inverse of the green value.
private void loadBitmapAndSetAlpha(int evtype, int id) {
BitmapFactory.Options bitopt=new BitmapFactory.Options();
bitopt.inMutable=true;
mOverlayBitmap[evtype] = BitmapFactory.decodeResource(getResources(), id, bitopt);
Bitmap bm = mOverlayBitmap[evtype];
int width = bm.getWidth();
int height = bm.getHeight();
for(int x = 0; x < width; x++)
{
for(int y = 0; y < height; y++)
{
int argb = bm.getPixel(x, y);
int green = (argb&0x0000ff00)>>8;
if(green>0)
{
int a = green;
a = (~green)&0xff;
argb &= 0x000000ff; // save only blue
argb |= a; // put alpha back in
bm.setPixel(x, y, argb);
}
}
}
}