0

I want to implement color detection in Android. What I exactly want to do is that after taking a picture with Android Camera, I want to detect the color of object in that picture. My goal is detecting colors according to color intensity. At this point, I searched and saw different approaches related to this goal. There are some algorithms which converts the images into Bitmap and then detects the colors, some of them use RGB. Additionally, I saw that OpenCV is also a known solution for this problem.

Now, I wonder which way I should follow. Which way is better for my case. Are there anyone who can help and direct me through a method?

Any help will be appreciated. Thank you all in advance.

cgbs
  • 83
  • 3
  • 13
  • See [this](http://stackoverflow.com/q/9443377/4385913) – Skizo-ozᴉʞS ツ Feb 23 '15 at 09:55
  • I've already seen that question but as I said I'm looking for a help to direct me through the best method for my own case. I am also not experienced with OpenCV. I'm a beginner. Thank you anyway. – cgbs Feb 23 '15 at 10:04
  • "Detecting colors according to color intensity" doesn't tell us very much. What's your end goal? Are you making a camera app for consumers? or camera app for scientists? are you trying to mimic a gallery app that will match the colors inside a picture? or something else entirely? – Stephan Branczyk Feb 23 '15 at 10:04
  • My main goal is to improve a useful application for blind people to enable them recognize object colors. So I need to detect the main color of the object in a taken picture. I mean I aim to detect the most dominant tone in the picture. – cgbs Feb 23 '15 at 10:07
  • After you detected this color, what do you want to do with it? Just display the value or do you want to change it somehow? – the-ginger-geek Feb 23 '15 at 10:20
  • I just want to inform user about what is color of the object that user wants to know and takes the picture of it to recognize. – cgbs Feb 23 '15 at 11:00
  • Look at the answer I gave and see if it helps – the-ginger-geek Feb 24 '15 at 04:59

1 Answers1

5

If I understand your question correctly, you want a method that will take an image and determine the most dominant color in that method and then return the result.

I have put together a class that should provide you with the result, don't know if it's the most efficient algorithm but give it a try. To implement the solution you can do the following

new ColorFinder(new CallbackInterface() {
    @Override
    public void onCompleted(String color) {
        Toast.makeText(context, "Your Color : " + color, Toast.LENGTH_SHORT).show();
    }
}).findDominantColor(yourBitmap);

ColorFinder.java

import android.graphics.Bitmap;
import android.os.AsyncTask;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by Neil on 15/02/23.
 */
public class ColorFinder {
    private static final String TAG = ColorFinder.class.getSimpleName();

    private CallbackInterface callback;

    public ColorFinder(CallbackInterface callback) {
        this.callback = callback;
    }

    public void findDominantColor(Bitmap bitmap) {
        new GetDominantColor().execute(bitmap);
    }

    private int getDominantColorFromBitmap(Bitmap bitmap) {
        int [] pixels = new int[bitmap.getWidth()*bitmap.getHeight()];
        bitmap.getPixels(pixels,0,bitmap.getWidth(),0,0, bitmap.getWidth(), bitmap.getHeight());

        Map<Integer, PixelObject> pixelList = getMostDominantPixelList(pixels);
        return getDominantPixel(pixelList);
    }

    private Map<Integer, PixelObject> getMostDominantPixelList(int [] pixels) {
        Map<Integer, PixelObject> pixelList = new HashMap<>();

        for (int pixel : pixels) {
            if (pixelList.containsKey(pixel)) {
                pixelList.get(pixel).pixelCount++;
            } else {
                pixelList.put(pixel, new PixelObject(pixel, 1));
            }
        }

        return pixelList;
    }

    private int getDominantPixel(Map<Integer, PixelObject> pixelList) {
        int dominantColor = 0;
        int largestCount = 0;
        for (Map.Entry<Integer, PixelObject> entry : pixelList.entrySet()) {
            PixelObject pixelObject = entry.getValue();

            if (pixelObject.pixelCount > largestCount) {
                largestCount = pixelObject.pixelCount;
                dominantColor = pixelObject.pixel;
            }
        }

        return dominantColor;
    }

    private class GetDominantColor extends AsyncTask<Bitmap, Integer, Integer> {

        @Override
        protected Integer doInBackground(Bitmap... params) {
            int dominantColor = getDominantColorFromBitmap(params[0]);
            return dominantColor;
        }

        @Override
        protected void onPostExecute(Integer dominantColor) {
            String hexColor = colorToHex(dominantColor);
            if (callback != null)
                callback.onCompleted(hexColor);
        }

        private String colorToHex(int color) {
            return String.format("#%06X", (0xFFFFFF & color));
        }
    }

    public interface CallbackInterface {
        public void onCompleted(String dominantColor);
    }
}

PixelObject.java

public class PixelObject {
    public int pixel;
    public int pixelCount;

    public PixelObject(int pixel, int pixelCount) {
        this.pixel = pixel;
        this.pixelCount = pixelCount;
    }
}
the-ginger-geek
  • 7,041
  • 4
  • 27
  • 45
  • First of all sorry for my late answer. I've tried to implement this solution but it gives a fatal exception like: ' FATAL EXCEPTION: main java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.burcu1.colordetectortrial/com.example.burcu1.colordetectortrial.ColorFinder}: java.lang.InstantiationException: can't instantiate class com.example.burcu1.colordetectortrial.ColorFinder; no empty constructor at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2106)' – cgbs Feb 25 '15 at 11:36
  • Have you tried debugging? Worked perfectly when I posted it. – the-ginger-geek Feb 25 '15 at 11:37
  • Yes, I runned it in debug mode but initially the app stops and gives that exception in logcat. Maybe I've implemented it in a wrong way? Before adding this solution to my app, I've had implemented the capturing an image and getting the uri of that image. Then I added your solution in another class "ColorFinder". I've put an intent in my main activity to go ColorFinder activity. And then, in onCreate function, I've taken the image uri, then I got the bitmap version of it. Then I run your solution in onCreate function as you said: – cgbs Feb 25 '15 at 11:57
  • `new ColorFinder(new CallbackInterface() { @Override public void onCompleted(String color) { Toast.makeText(getApplicationContext(), "Your Color : " + color, Toast.LENGTH_SHORT).show(); } }).findDominantColor(imageBitmap);` Did I make a mistake in the implementation? – cgbs Feb 25 '15 at 11:58
  • Looks like an error in the implementation. I'll have to see how you implemented the code to be able to help. – the-ginger-geek Feb 25 '15 at 12:02
  • I called the ColorFinder activity in **MainActivity.java** like this: `private void callColorFinder() { //Bitmap bitmap = getBitmap(imageUri); Intent findColor = new Intent(this, ColorFinder.class); findColor.putExtra("imageUri", imageUri); startActivityForResult(findColor, REQUEST_COLOR_FINDER); }` For my ColorFinder class I will write things that I added to your original solution: – cgbs Feb 25 '15 at 12:07
  • `@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); comingIntent = getIntent(); imageUri = comingIntent.getStringExtra("imageUri"); imageBitmap = getBitmap(Uri.parse(imageUri)); new ColorFinder(new CallbackInterface() { @Override public void onCompleted(String color) { Toast.makeText(getApplicationContext(), "Your Color : " + color, Toast.LENGTH_SHORT).show(); } }).findDominantColor(imageBitmap); }` – cgbs Feb 25 '15 at 12:08
  • And my getBitmap function: `private Bitmap getBitmap(Uri fileUri) { // bimatp factory BitmapFactory.Options options = new BitmapFactory.Options(); // downsizing image as it throws OutOfMemory Exception for larger images options.inSampleSize = 8; final Bitmap bitmap = BitmapFactory.decodeFile(fileUri.toString(),options); return bitmap; }` – cgbs Feb 25 '15 at 12:10
  • Did you define the ColorFinder class as an activity? – the-ginger-geek Feb 25 '15 at 12:11
  • Yes, I'm really new in Android. This will be my second application. How should I implement it? – cgbs Feb 25 '15 at 12:13
  • I'll post a example project on github – the-ginger-geek Feb 25 '15 at 12:15
  • If it works please mark as the answer. Hope you're using android studio because this is an android studio project. It's called ImageAnalyser https://github.com/NeilHogg69/Android-Tutorials – the-ginger-geek Feb 25 '15 at 12:29
  • To apply luminance detection you will have to modify the algorithm. At the moment the algorithm finds the pixel with the highest density and returns that color. This does not return the pixel with the highest luminance. To find the pixel with the highest luminance you will have to take the formula and add a check for luminance. Here is a relevant question for that http://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color – the-ginger-geek Feb 25 '15 at 12:43
  • You helped me so much, thank you again! I've refined my app by correcting ColorFinder class as not activity and then importing it to my main activity as you did in your project. Now it works and returns me a hex code like #90B78F. I've searched and saw that I can convert this value to RGB or HSV. In order to get an accurate result in color naming, which method should I use? Because I want to detect every color tone – cgbs Feb 25 '15 at 13:42
  • I added the luminance detection to the source. I think that's what you actually wanted? You can update your source and run the app. It'll give you two results from an image. The Luminance and the Density. As for the other question. I think you should create a new question on stackoverflow and send me the link. – the-ginger-geek Feb 25 '15 at 13:44
  • I've updated my source and I could be able to detect both with density and luminance. It shows the tones and hex codes. What I exactly want is that I want to be able to detect every tone and say the name of that tone as a string. I will create a new question as you suggest. – cgbs Feb 25 '15 at 16:57
  • Here is the link: – cgbs Feb 25 '15 at 17:09
  • Hi again Neil, I've really improved my application. Now, I want to run it in my phone, I'm getting an error like "java.lang.RuntimeException: An error occured while executing doInBackground() ... Caused by: java.lang.StackOverflowError" in your PixelDensity.java class(at 85. and 114. lines) although it works fine on the emulator. Both my emulator and my phone is 4.2.2 and API 17. What can be the reason for it? How can I solve this problem? If you can help me, I will be really so appreciated. Thank you in advance. – cgbs Mar 15 '15 at 21:32
  • Please ask a new question and post the link as a comment here – the-ginger-geek Mar 16 '15 at 04:20
  • [you can find it here](http://stackoverflow.com/questions/29073023/stackoverflowerror-of-hashmap-entry-iterator-in-recursive-function) – cgbs Mar 16 '15 at 09:09
  • Can I ask you something? In your PixelDensity.java class, why did you define ACCEPT_DIFFERENTIAL_THRESHOLD as 40? I debugged the app and because the comparison value comes smaller than that value, it face with a infinite loop and gives stackoverflow error. – cgbs Mar 21 '15 at 16:14
  • That value can be anything. It's very simple. – the-ginger-geek Mar 21 '15 at 16:15
  • Anything? Then, why are you putting a comparison value if that limit does not matter? – cgbs Mar 21 '15 at 16:22