Extract colors from image(bitmap) using Palette in Android

When open the profile page of any person or group in Whatsapp, you will find that the color of the toolbar at top uses the color from the DP of the user or group. It looks good when you use extracted colors from image in your layout. Your layout will blend with the image and other content.

So here we can extract different colors from any image(bitmap) used in our application and use those colors effectively in our app throughout.

Screenshot_1468609451

Main Color Profiles

First of all there are main 6 type of color profiles in the image that we can extract.

  • Vibrant
  • Vibrant Dark
  • Vibrant Light
  • Muted
  • Muted Dark
  • Muted Light

Most used profile from above are Vibrant and Vibrant Dark. The reason is, vibrant colors are pure, bright, intense, saturated and high chroma color. They stand out in the image and easy to point out. Now if we use those colors in the layout along with the image, it will give a great look. Whatsapp is the best example of it as we discussed earlier.

Every profile(swatch) gives us following information that we can use in the application.

  • population of that color in image
  • hue, saturation, lightness
  • body text color with sufficient contrast with the main color
  • title text color with sufficient contrast with the main color

Now we look at the code to get the Palette from bitmap.

Bitmap b = null;
b = BitmapFactory.decodeResource(getResources(), R.drawable.img1);
Palette p = Palette.from(b).generate();

Here we get the Palette from image(bitmap). Now we extract main 6 different kind of color profile(swatch) from it.

Palette.Swatch psVibrant = p.getVibrantSwatch();
Palette.Swatch psVibrantLight = p.getLightVibrantSwatch();
Palette.Swatch psVibrantDark = p.getDarkVibrantSwatch();
Palette.Swatch psMuted = p.getMutedSwatch();
Palette.Swatch psMutedLight = p.getLightMutedSwatch();
Palette.Swatch psMutedDark = p.getDarkMutedSwatch();

Now we will extract 4 different values from these swatch as described earlier in the article. Here we will take psVibrant as an example.

int color = psVibrant.getRgb();
int population = psVibrant.getPopulation();
float[] hsl = psVibrant.getHsl();
int bodyTextColor = psVibrant.getBodyTextColor();
int titleTextColor = psVibrant.getTitleTextColor();

You can also get other colors from image other than above 6 by following code snippet.

List<Palette.Swatch> pss;
pss = p.getSwatches();
for(int j = 0; j < pss.size(); j++) {
  Palette.Swatch ps = pss.get(j);
  int color = ps.getRgb();
  int population = ps.getPopulation();
  float[] hsl = ps.getHsl();
  int bodyTextColor = ps.getBodyTextColor();
  int titleTextColor = ps.getTitleTextColor();
}
Screenshot_1468609457

Other Color Profiles

Advertisements

Rotate image(bitmap) to any angle in Android

In many image related Android applications, we may need to rotate the bitmap(not ImageView). Here I added the code below to rotate the image at any possible angle using Matrix.

Screenshot_20160619-211636

rotation of image resulted from below code

Bitmap bInput/*your input bitmap*/, bOutput;
float degrees = 45;//rotation degree
Matrix matrix = new Matrix();
matrix.setRotate(degrees);
bOutput = Bitmap.createBitmap(bInput, 0, 0, bInput.getWidth(), bInput.getHeight(), matrix, true);

Flip image(bitmap) horizontally and vertically in Android

In any photo related Android application, flipping image is necessary feature. So here I wrote the code to flip the image in both horizontal as well as vertical direction.

Screenshot_20160619-211419

Result of the below code

Code for vertical flip bitmap:

Bitmap bInput/*your input bitmap*/, bOutput;
Matrix matrix = new Matrix();
matrix.preScale(1.0f, -1.0f);
bOutput = Bitmap.createBitmap(bInput, 0, 0, bInput.getWidth(), bInput.getHeight(), matrix, true);

Code for horizontal flip bitmap:

Bitmap bInput/*your input bitmap*/, bOutput;
Matrix matrix = new Matrix();
matrix.preScale(-1.0f, 1.0f);
bOutput = Bitmap.createBitmap(bInput, 0, 0, bInput.getWidth(), bInput.getHeight(), matrix, true);

Upload image from Android app to server using PHP without any library

Image uploading is necessary in any Android app now a days.
We can upload image to our server from the Android app. Here I used PHP at the server side to catch and save the image file.

Here we did it without using any kind of external library. So you can directly use it in your project without any hassle.

First we pick the image from the gallery. You can checkout my article, “Pick image from gallery before and after KITKAT version in Android” and get the path of the image file in the “path” variable.

Then use following code snippet to upload that image to the server.

String lineEnd = "\r\n";
String twoHyphens = "--";
String boundary = "*****";
try {
  connectURL = new URL("http://example.com/imageupload.php");
  HttpURLConnection conn = (HttpURLConnection) connectURL.openConnection();
  conn.setDoInput(true);
  conn.setDoOutput(true);
  conn.setUseCaches(false);
  conn.setRequestMethod("POST");
  conn.setRequestProperty("Connection", "Keep-Alive");
  conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);

  DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
  dos.writeBytes(twoHyphens + boundary + lineEnd);

  HashMap<String, String> params = new HashMap<>();
  params.put("key", "value");	//post params that you want yo send
  StringBuilder result = new StringBuilder();
  for (Map.Entry<String, String> entry : params.entrySet()) {
    result.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + lineEnd);
    result.append(lineEnd);
    result.append(entry.getValue());
    result.append(lineEnd);
    result.append(twoHyphens + boundary + lineEnd);
  }

  dos.writeBytes(result.toString());
  if (!String.valueOf("").equals(path)) {
    dos.writeBytes("Content-Disposition: form-data; name=\"" + "picture" + "\";filename=\"" + "picture.jpg" + "\"" + lineEnd);
    dos.writeBytes(lineEnd);
    fis = new FileInputStream(path);
    int bytesAvailable = fis.available();

    int maxBufferSize = 8192;
    int bufferSize = Math.min(bytesAvailable, maxBufferSize);
    byte[] buffer = new byte[bufferSize];

    int bytesRead = fis.read(buffer, 0, bufferSize);

    while (bytesRead > 0) {
      dos.write(buffer, 0, bufferSize);
      bytesAvailable = fis.available();
      bufferSize = Math.min(bytesAvailable, maxBufferSize);
      bytesRead = fis.read(buffer, 0, bufferSize);
    }
    dos.writeBytes(lineEnd);
    dos.writeBytes(twoHyphens + boundary + lineEnd);
    fis.close();
  }
  dos.flush();

  InputStream is = conn.getInputStream();

  int ch;

  StringBuffer b = new StringBuffer();
  while ((ch = is.read()) != -1) {
    b.append((char) ch);
  }
  json = b.toString();
  dos.close();
} catch (MalformedURLException ex) {
} catch (IOException ioe) {
}

Make sure that you call this code snippet inside of AsyncTask or Thread. Once you get the response from the server, your image has been uploaded to the server.

Now here is the code for PHP to move the uploaded to the same directory as php file.

    move_uploaded_file($_FILES['picture']['tmp_name'], $_FILES['picture']['name']);

And done! You can find your uploaded image in “images” directory on your server.

Pick image from gallery before and after KITKAT version in Android

In the android application, we need to pick any image from our gallery. For that android provides some in-built intent action that can fetch us image from gallery. After Android Kitkat, Google made few changes so that we can pick image from different applications.

Screenshot_2016-04-25-00-20-37

The following code will open Default Picker app for KitKat, Lollipop, Marshmallow and Android N users and Gallery app for before Kitkat users to pick image.

Update:
The previous code sometimes failed to get image from external sdcard, Google Drive or sometime from internal storage too. So I did some research and integrate few answers from Stackoverflow and come up with a new solution which able to pick image from every things from that Default Picker App after Kitkat devices displayed in the image.

if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
  Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
  intent.addCategory(Intent.CATEGORY_OPENABLE);
  intent.setType("image/*");
  startActivityForResult(Intent.createChooser(intent, "Select Picture"), 1);
} else {
  Intent intent = new Intent();
  intent.setType("image/*");
  intent.setAction(Intent.ACTION_GET_CONTENT);
  startActivityForResult(Intent.createChooser(intent, "Select Picture"), 1);
}

Now after selecting image, we need to handle it properly to display it to ImageView or we need to get it’s path if we want to do any other operation on it. Here is the code for onActivityResult.

@TargetApi(19)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (data != null && data.getData() != null && resultCode == RESULT_OK) {
    boolean isImageFromGoogleDrive = false;
    Uri uri = data.getData();

    if (isKitKat && DocumentsContract.isDocumentUri(TestActivity.this, uri)) {
      if ("com.android.externalstorage.documents".equals(uri.getAuthority())) {
        String docId = DocumentsContract.getDocumentId(uri);
        String[] split = docId.split(":");
        String type = split[0];

        if ("primary".equalsIgnoreCase(type)) {
          imgPath = Environment.getExternalStorageDirectory() + "/" + split[1];
        }
        else {
          Pattern DIR_SEPORATOR = Pattern.compile("/");
          Set<String> rv = new HashSet<>();
          String rawExternalStorage = System.getenv("EXTERNAL_STORAGE");
          String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
          String rawEmulatedStorageTarget = System.getenv("EMULATED_STORAGE_TARGET");
          if(TextUtils.isEmpty(rawEmulatedStorageTarget))
          {
            if(TextUtils.isEmpty(rawExternalStorage))
            {
              rv.add("/storage/sdcard0");
            }
            else
            {
              rv.add(rawExternalStorage);
            }
          }
          else
          {
            String rawUserId;
            if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
            {
              rawUserId = "";
            }
            else
            {
              String path = Environment.getExternalStorageDirectory().getAbsolutePath();
              String[] folders = DIR_SEPORATOR.split(path);
              String lastFolder = folders[folders.length - 1];
              boolean isDigit = false;
              try
              {
                Integer.valueOf(lastFolder);
                isDigit = true;
              }
              catch(NumberFormatException ignored)
              {
              }
              rawUserId = isDigit ? lastFolder : "";
            }
            if(TextUtils.isEmpty(rawUserId))
            {
              rv.add(rawEmulatedStorageTarget);
            }
            else
            {
              rv.add(rawEmulatedStorageTarget + File.separator + rawUserId);
            }
          }
          if(!TextUtils.isEmpty(rawSecondaryStoragesStr))
          {
            String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
            Collections.addAll(rv, rawSecondaryStorages);
          }
          String[] temp = rv.toArray(new String[rv.size()]);
          for (int i = 0; i < temp.length; i++)   {
            File tempf = new File(temp[i] + "/" + split[1]);
            if(tempf.exists()) {
              imgPath = temp[i] + "/" + split[1];
            }
          }
        }
      }
      else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
        String id = DocumentsContract.getDocumentId(uri);
        Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

        Cursor cursor = null;
        String column = "_data";
        String[] projection = { column };
        try {
          cursor = TestActivity.this.getContentResolver().query(contentUri, projection, null, null,
null);
          if (cursor != null && cursor.moveToFirst()) {
            int column_index = cursor.getColumnIndexOrThrow(column);
            imgPath = cursor.getString(column_index);
          }
        } finally {
          if (cursor != null)
            cursor.close();
        }
      }
      else if("com.android.providers.media.documents".equals(uri.getAuthority())) {
        String docId = DocumentsContract.getDocumentId(uri);
        String[] split = docId.split(":");
        String type = split[0];

        Uri contentUri = null;
        if ("image".equals(type)) {
          contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        } else if ("video".equals(type)) {
          contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
        } else if ("audio".equals(type)) {
          contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        }

        String selection = "_id=?";
        String[] selectionArgs = new String[]{ split[1] };

        Cursor cursor = null;
        String column = "_data";
        String[] projection = { column };

        try {
          cursor = TestActivity.this.getContentResolver().query(contentUri, projection, selection, selectionArgs, null);
          if (cursor != null && cursor.moveToFirst()) {
            int column_index = cursor.getColumnIndexOrThrow(column);
            imgPath = cursor.getString(column_index);
          }
        } finally {
          if (cursor != null)
            cursor.close();
        }
      }
      else if("com.google.android.apps.docs.storage".equals(uri.getAuthority()))   {
        isImageFromGoogleDrive = true;
      }
    }
    else if ("content".equalsIgnoreCase(uri.getScheme())) {
      Cursor cursor = null;
      String column = "_data";
      String[] projection = { column };

      try {
        cursor = TestActivity.this.getContentResolver().query(uri, projection, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
          int column_index = cursor.getColumnIndexOrThrow(column);
          imgPath = cursor.getString(column_index);
        }
      }
      finally {
        if (cursor != null)
          cursor.close();
      }
    }
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
      imgPath = uri.getPath();
    }

    if(isImageFromGoogleDrive)  {
      try {
        Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
        img.setImageBitmap(bitmap);
      }
      catch (Exception e) {
        e.printStackTrace();
      }
    }
    else    {
      File f = new File(imgPath);
      BitmapFactory.Options bmOptions = new BitmapFactory.Options();
      Bitmap bitmap = BitmapFactory.decodeFile(f.getAbsolutePath(),bmOptions);
      img.setImageBitmap(bitmap);
    }
  }
  super.onActivityResult(requestCode, resultCode, data);
}

Here, images from Google Drive may not be on our device so we directly us InputStream of that image from the Google Drive and show it in the ImageView without getting it’s path. You can save it in your storage if you need to use it for any further requirement.

Please comments below if you find any mistake or having problem in implementing this code.

Download Source Code: https://github.com/dakshbhatt21/a-computer-engineer

Resize image during decode from file to Bitmap in android

java.lang.OutOfMemoryError is the nightmare for any android developer. During image operation we may get this out of memory exception any time because our app uses all the memory allocated by android system to that app so it will give you out of memory error.

We encounter this error when we try to decode larger image using its path of sdcard to Bitmap. So we can skip this error by decoding image to smaller size.

We can avoid this problem or better to say restrict it to a level by creating Bitmap of required size and not the actual size.

Images taken from camera now a days are of more than 3000 and 4000 in resolution. So if we want to display them in GridView or ListView, we have to sample them down so it won’t create OOM or does not hang the UI.

For that we will use sample size to BitmapFactory.Options while decoding the image. It will load your image in smaller size compare to the original image and save your memory.

Google suggest that sample size should be in power of 2 so the below code contains the logic of creating sample size according to the maximum size you provide and then it will load your image with the resolution near to that maximum size.

Here is the direct function I create that you can use in your project by providing the file path of your image. You can find out how get the file path of any image from this article: Pick image from gallery before and after Kitkat version in Android

private Bitmap decodeFile(String imgPath)
{
  Bitmap b = null;
  int max_size = 1000;
  File f = new File(imgPath);
  try {
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;
    FileInputStream fis = new FileInputStream(f);
    BitmapFactory.decodeStream(fis, null, o);
    fis.close();
    int scale = 1;
    if (o.outHeight > max_size || o.outWidth > max_size)
    {
      scale = (int) Math.pow(2, (int) Math.ceil(Math.log(max_size / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
    }
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize = scale;
    fis = new FileInputStream(f);
    b = BitmapFactory.decodeStream(fis, null, o2);
    fis.close();
  }
  catch (Exception e)
  {
  }
  return b;
}

Share your views in comments!

How to draw Canvas on ImageView and save Canvas as Bitmap and store in sdcard in Android

In some of the application we draw image, text and line on Canvas object in Android. Canvas is the basic component in android for drawing objects on it.

#1 Create Canvas and display it in ImageView.
Now we create a Canvas and display its content in ImageView.

  public class MyCanvas extends View {
    public MyCanvas(Context context) {
      super(context);
      // TODO Auto-generated constructor stub
    }

    @Override
    protected void onDraw(Canvas canvas) {
      // TODO Auto-generated method stub
      super.onDraw(canvas);
      Paint pBackground = new Paint();
      pBackground.setColor(Color.WHITE);
      canvas.drawRect(0, 0, 512, 512, pBackground);
      Paint pText = new Paint();
      pText.setColor(Color.BLACK);
      pText.setTextSize(20);
      canvas.drawText("Sample Text", 100, 100, pText);
    }
  }

Now set this canvas to your ImageView using following code.

  View v = new MyCanvas(getApplicationContext());
  Bitmap bitmap = Bitmap.createBitmap(500/*width*/, 500/*height*/, Bitmap.Config.ARGB_8888);
  Canvas canvas = new Canvas(bitmap);
  v.draw(canvas);
  ImageView iv = (ImageView) findViewById(R.id.iv);
  iv.setImageBitmap(bitmap);

You can see “Sample Text” in ImageView.

#2 Save canvas bitmap to sdcard.
We will save this bitmap to sdcard so all your drawing on canvas will be stored as image.

Bitmap b = null;

//create directory if not exist
File dir = new File("/sdcard/tempfolder/");
if (!dir.exists()) {
  dir.mkdirs();
}

File output = new File(dir, "tempfile.jpg");
OutputStream os = null;

try {
  os = new FileOutputStream(output);
  b.compress(Bitmap.CompressFormat.JPEG, 100, os);
  os.flush();
  os.close();

  //this code will scan the image so that it will appear in your gallery when you open next time
  MediaScannerConnection.scanFile(this,	new String[] { output.toString() }, null,
    new MediaScannerConnection.OnScanCompletedListener() {
      public void onScanCompleted(String path, Uri uri) {
        Log.d("appname", "image is saved in gallery and gallery is refreshed.");
      }
    }
  );
} catch (Exception e) {
}

If you find any problem or doubt, please mention in comments. Do not forget to share!

Download Source Code: https://github.com/dakshbhatt21/a-computer-engineer