How to programmatically change contrast of a bitmap in android?

51,821

Solution 1

Try this. Your code didn't work because you create only a mutable bitmap and didn't copy the image of source bitmap to the mutable one if i'm not mistaken.

Hope It helps :)

private Bitmap adjustedContrast(Bitmap src, double value)
{
    // image size
    int width = src.getWidth();
    int height = src.getHeight();
    // create output bitmap

    // create a mutable empty bitmap
    Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());

    // create a canvas so that we can draw the bmOut Bitmap from source bitmap
    Canvas c = new Canvas();
    c.setBitmap(bmOut);

    // draw bitmap to bmOut from src bitmap so we can modify it
    c.drawBitmap(src, 0, 0, new Paint(Color.BLACK));


    // color information
    int A, R, G, B;
    int pixel;
    // get contrast value
    double contrast = Math.pow((100 + value) / 100, 2);

    // scan through all pixels
    for(int x = 0; x < width; ++x) {
        for(int y = 0; y < height; ++y) {
            // get pixel color
            pixel = src.getPixel(x, y);
            A = Color.alpha(pixel);
            // apply filter contrast for every channel R, G, B
            R = Color.red(pixel);
            R = (int)(((((R / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
            if(R < 0) { R = 0; }
            else if(R > 255) { R = 255; }

            G = Color.green(pixel);
            G = (int)(((((G / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
            if(G < 0) { G = 0; }
            else if(G > 255) { G = 255; }

            B = Color.blue(pixel);
            B = (int)(((((B / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
            if(B < 0) { B = 0; }
            else if(B > 255) { B = 255; }

            // set new pixel color to output bitmap
            bmOut.setPixel(x, y, Color.argb(A, R, G, B));
        }
    }
    return bmOut;
}

Solution 2

Here is complete method:

/**
 * 
 * @param bmp input bitmap
 * @param contrast 0..10 1 is default
 * @param brightness -255..255 0 is default
 * @return new bitmap
 */
public static Bitmap changeBitmapContrastBrightness(Bitmap bmp, float contrast, float brightness)
{
    ColorMatrix cm = new ColorMatrix(new float[]
            {
                contrast, 0, 0, 0, brightness,
                0, contrast, 0, 0, brightness,
                0, 0, contrast, 0, brightness,
                0, 0, 0, 1, 0
            });

    Bitmap ret = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());

    Canvas canvas = new Canvas(ret);

    Paint paint = new Paint();
    paint.setColorFilter(new ColorMatrixColorFilter(cm));
    canvas.drawBitmap(bmp, 0, 0, paint);

    return ret;
}

Solution 3

We can use seek bar to adjust contrast.

enter image description here

MainActivity.java:

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    ImageView imageView;
    SeekBar seekbar;
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.image);
        textView = (TextView) findViewById(R.id.label);

        seekbar = (SeekBar) findViewById(R.id.seekbar);
        seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
                imageView.setImageBitmap(changeBitmapContrastBrightness(BitmapFactory.decodeResource(getResources(), R.drawable.lhota), (float) progress / 100f, 1));
                textView.setText("Contrast: "+(float) progress / 100f);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {}

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {}
        });

        seekbar.setMax(200);
        seekbar.setProgress(100);
    }

    public static Bitmap changeBitmapContrastBrightness(Bitmap bmp, float contrast, float brightness) {
        ColorMatrix cm = new ColorMatrix(new float[]
                {
                        contrast, 0, 0, 0, brightness,
                        0, contrast, 0, 0, brightness,
                        0, 0, contrast, 0, brightness,
                        0, 0, 0, 1, 0
                });

        Bitmap ret = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());

        Canvas canvas = new Canvas(ret);

        Paint paint = new Paint();
        paint.setColorFilter(new ColorMatrixColorFilter(cm));
        canvas.drawBitmap(bmp, 0, 0, paint);

        return ret;
    }
}

activity_main.java:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/label"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:textAppearance="@android:style/TextAppearance.Holo.Medium" />

    <SeekBar
        android:id="@+id/seekbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

Solution 4

Here is a Renderscript implementation (from the Gradle Example Projects)

ip.rsh

#pragma version(1)
#pragma rs java_package_name(your.app.package)

contrast.rs

#include "ip.rsh"

static float brightM = 0.f;
static float brightC = 0.f;

void setBright(float v) {
    brightM = pow(2.f, v / 100.f);
    brightC = 127.f - brightM * 127.f;
}

void contrast(const uchar4 *in, uchar4 *out)
{
#if 0
    out->r = rsClamp((int)(brightM * in->r + brightC), 0, 255);
    out->g = rsClamp((int)(brightM * in->g + brightC), 0, 255);
    out->b = rsClamp((int)(brightM * in->b + brightC), 0, 255);
#else
    float3 v = convert_float3(in->rgb) * brightM + brightC;
    out->rgb = convert_uchar3(clamp(v, 0.f, 255.f));
#endif
}

Java

private Bitmap changeBrightness(Bitmap original RenderScript rs) {
    Allocation input = Allocation.createFromBitmap(rs, original);
    final Allocation output = Allocation.createTyped(rs, input.getType());
    ScriptC_contrast mScript = new ScriptC_contrast(rs);
    mScript.invoke_setBright(50.f);
    mScript.forEach_contrast(input, output);
    output.copyTo(original);
    return original;
}

Solution 5

Assumption - ImageView is used to display the bitmap

I did a more enhancement in the Ruslan's answer

Instead of replacing bitmap every time (which makes it slow if you are using seek bar) we can work on the Color Filter of the ImageView.

float contrast;
float brightness = 0;
ImageView imageView;

// SeekBar ranges from 0 to 90
// contrast ranges from 1 to 10  
mSeekBarContrast.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
            contrast = (float) (i + 10) / 10;
            // Changing the contrast of the bitmap
            imageView.setColorFilter(getContrastBrightnessFilter(contrast,brightness));
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {

        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {

        }
});

ColorMatrixColorFilter getContrastBrightnessFilter(float contrast, float brightness) {
    ColorMatrix cm = new ColorMatrix(new float[]
            {
                    contrast, 0, 0, 0, brightness,
                    0, contrast, 0, 0, brightness,
                    0, 0, contrast, 0, brightness,
                    0, 0, 0, 1, 0
            });
    return new ColorMatrixColorFilter(cm);
}

P.S - Brightness can also be changed along with contrast using this method

Share:
51,821
Sanchit Paurush
Author by

Sanchit Paurush

Passionately curious to do new things.

Updated on May 08, 2020

Comments

  • Sanchit Paurush
    Sanchit Paurush about 4 years

    I want to programmatically change the contrast of bitmap. Till now I have tried this.

    private Bitmap adjustedContrast(Bitmap src, double value)
        {
            // image size
            int width = src.getWidth();
            int height = src.getHeight();
            // create output bitmap
            Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
            // color information
            int A, R, G, B;
            int pixel;
            // get contrast value
            double contrast = Math.pow((100 + value) / 100, 2);
    
            // scan through all pixels
            for(int x = 0; x < width; ++x) {
                for(int y = 0; y < height; ++y) {
                    // get pixel color
                    pixel = src.getPixel(x, y);
                    A = Color.alpha(pixel);
                    // apply filter contrast for every channel R, G, B
                    R = Color.red(pixel);
                    R = (int)(((((R / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
                    if(R < 0) { R = 0; }
                    else if(R > 255) { R = 255; }
    
                    G = Color.green(pixel);
                    G = (int)(((((G / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
                    if(G < 0) { G = 0; }
                    else if(G > 255) { G = 255; }
    
                    B = Color.blue(pixel);
                    B = (int)(((((B / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
                    if(B < 0) { B = 0; }
                    else if(B > 255) { B = 255; }
    
                    // set new pixel color to output bitmap
                    bmOut.setPixel(x, y, Color.argb(A, R, G, B));
                }
            }
            return bmOut;
        }
    

    But this does not work as expected. Please help me in this or provide any other solution to achieve this. Thanks in advance.

    • devsnd
      devsnd over 11 years
      what doesn't work as expected? please elaborate more detailed what does work and what doesnt.
  • Sanchit Paurush
    Sanchit Paurush over 11 years
    Thanks for your reply. I tried this. But this code works with canvas only and I cannot retrieve the changed image from canvas. So is there any other way for it.
  • kdroider
    kdroider over 11 years
    I edited the link above. I hope that will be more helpful in your case. :)
  • Sanchit Paurush
    Sanchit Paurush over 11 years
    It is the same code that I gave above. It always make my image black and white and does not make it back to its original colors. Please provide any other.
  • Aron Lorincz
    Aron Lorincz over 10 years
    This's the fastest solution I found so far. Probably only a renderscript-based one would be faster...
  • Gusten
    Gusten over 10 years
    Awesome, this method processes my image in about a second, above method took about 20 seconds.
  • mnsalim
    mnsalim about 10 years
    what is the max & min value of contrast & brightness. I use -255 t0 255 for brightness & -100 to 100 for contrast, but i get wrong result. I also try to use value 0 for contrast while some value for brightness i get a black image.Please help me. I got this is the fastest method, but it give me wrong result, definitely i do something wrong, it would be nice if any one correct me.
  • Vasiliy Kulakov
    Vasiliy Kulakov over 9 years
    Thanks, but I don't think we need a Canvas here -- we're writing to the bitmap bmOut directly.
  • vianna77
    vianna77 over 8 years
    Just a improvement to the code due to the constant break on the bmp.getConfig... Bitmap.Config config = src.getConfig(); if (config == null) { config = Bitmap.Config.ARGB_8888; } Bitmap ret = Bitmap.createBitmap(src.getWidth(), src.getHeight(), config);
  • Andrew F
    Andrew F over 8 years
    Can you post more completed example?
  • Patrick
    Patrick over 8 years
    the sample is pretty complete, for integrating it see the link, bottom of the page with full projects
  • Andrew F
    Andrew F over 8 years
    anyway can you post direct link to full project? :) I found only one link and it's unavailable
  • Patrick
    Patrick over 8 years
    They seem to have removed the link to the projects, just search how to create an app with renderscript, the rest should be clear
  • V I J E S H
    V I J E S H almost 7 years
    This will create OutOfMemmory if we are changing the brightness through silder
  • Kishan Donga
    Kishan Donga almost 4 years
    what is max and min values can update your answer with this values
  • M DEV
    M DEV almost 2 years
    This is best method to make contrast and brightness to the image. It works faster then other tools. Thank you so much
  • M DEV
    M DEV almost 2 years
    now, it is working very fine. If we seek back from 50 then the image gets dark and if we seek more from 50 then image gets bright.