I am not a Android developer, but my team members required the same thing I did on the web. I required a function to which I pass any three colors (e.g. red,blue,green), and I will pass a count, e.g. 100.
Definition of function
function getColorArray(mincolor,midcolor,maxcolor,100){
return colorarray;
}
When I have to call function:
getColorArray(red,yellow,green,100)
So it will give a array of 100 colors from a red,blue,green color scale.
I did it in Javascript. Here is the fiddle link.
I want the same output in Android.
This code does a simple line interpolation (c1 - c2, c2 - c3) . Your example JS code has richer options than this simple example (non linear interpolations), but I think this should help you get started.
You should probably define some custom colors if you're going to let the users name the colors - the default range of system colors is pretty limited (at least with java.awt.Color predifined colors, that is).
import java.awt.*;
import javax.swing.*;
import java.lang.reflect.Field;
public class ColorTest {
public static void main(String[] args) {
int n = args.length > 0 ? Integer.parseInt(args[0]) : 5;
Color[] test = getColorArray("red", "green", "blue", n);
for(Color c : test) {
System.out.println(c);
}
}
public static Color[] getColorArray(String c1, String c2, String c3, int n) {
Color[] inputColors = new Color[3];
try {
Field field1 = Color.class.getField(c1);
Field field2 = Color.class.getField(c2);
Field field3 = Color.class.getField(c3);
inputColors[0] = (Color) field1.get(null);
inputColors[1] = (Color) field2.get(null);
inputColors[2] = (Color) field3.get(null);
} catch (Exception e) {
System.err.println("One of the color values is not defined!");
System.err.println(e.getMessage());
return null;
}
Color[] result = new Color[n];
int[] c1RGB = { inputColors[0].getRed(), inputColors[0].getGreen(), inputColors[0].getBlue() };
int[] c2RGB = { inputColors[1].getRed(), inputColors[1].getGreen(), inputColors[1].getBlue() };
int[] c3RGB = { inputColors[2].getRed(), inputColors[2].getGreen(), inputColors[2].getBlue() };
int[] tmpRGB = new int[3];
tmpRGB[0] = c2RGB[0] - c1RGB[0];
tmpRGB[1] = c2RGB[1] - c1RGB[1];
tmpRGB[2] = c2RGB[2] - c1RGB[2];
float mod = n/2.0f;
for (int i = 0; i < n/2; i++) {
result[i] = new Color(
(int) (c1RGB[0] + i/mod*tmpRGB[0]) % 256,
(int) (c1RGB[1] + i/mod*tmpRGB[1]) % 256,
(int) (c1RGB[2] + i/mod*tmpRGB[2]) % 256
);
}
tmpRGB[0] = c3RGB[0] - c2RGB[0];
tmpRGB[1] = c3RGB[1] - c2RGB[1];
tmpRGB[2] = c3RGB[2] - c2RGB[2];
for (int i = 0; i < n/2 + n%2; i++) {
result[i+n/2] = new Color(
(int) (c2RGB[0] + i/mod*tmpRGB[0]) % 256,
(int) (c2RGB[1] + i/mod*tmpRGB[1]) % 256,
(int) (c2RGB[2] + i/mod*tmpRGB[2]) % 256
);
}
return result;
}
}
Related
I want to convert an image from Android camera to HSI format using OpenCV.
The problem is when I use the following method
private Mat rgb2hsi(Mat rgbFrame) {
Mat hsiFrame = rgbFrame.clone();
for( int i = 0; i < rgbFrame.rows(); ++i ) {
for( int j = 0; j < rgbFrame.cols(); ++j ) {
double[] rgb = rgbFrame.get(i, j);
Log.d(MAINTAG, "rgbFrame.get(i, j) array size = " + rgb.length);
double colorR = rgb[0];
double colorG = rgb[1];
double colorB = rgb[2];
double minRGB = min(colorR, colorG, colorB);
double colorI = (colorR + colorG + colorB) / 3;
double colorS = 0.0;
if(colorI > 0) colorS = 1.0 - (minRGB / colorI);
double colorH;
double const1 = colorR - (colorG / 2) - (colorB / 2);
double const2 = Math.sqrt(Math.pow(colorR, 2) + Math.pow(colorG, 2) + Math.pow(colorR, 2)
- (colorR * colorG) - (colorR * colorB) - (colorG * colorB));
colorH = Math.acos(const1 / const2);
if(colorB > colorG) colorH = 360 - colorH;
double[] hsi = {colorH, colorS, colorI};
hsiFrame.put(i, j, hsi);
}
}
return hsiFrame;
}
It shows an error
java.lang.UnsupportedOperationException: Provided data element number (3) should be multiple of the Mat channels count (4)
I search for a while to figure out the cause of this error.
I found that I put an array of size 3 instead of 4.
Android convert byte array from Camera API to color Mat object openCV
I wonder what Type of image receive from Android Camera.
Why when I get an array of size 4?
How to convert an image received from Android camera to HSI and preview on the screen?
The following is the overrided method onCameraFrame
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
Mat outputFrame = inputFrame.rgba();
/* Get RGB color from the pixel at [index_row, index_column] */
int index_row = 0;
int index_column = 0;
final double[] mRgb_pixel = outputFrame.get(index_row, index_column);
/* Show the result */
runOnUiThread(new Runnable() {
#Override
public void run() {
int r = (int) mRgb_pixel[0];
int g = (int) mRgb_pixel[1];
int b = (int) mRgb_pixel[2];
/* Set RGB color */
mRred_textview.setText("Red\n" + Double.toString(mRgb_pixel[0]));
mGreen_textview.setText("Green\n" + Double.toString(mRgb_pixel[1]));
mBlue_textview.setText("Blue\n" + Double.toString(mRgb_pixel[2]));
mColor_textview.setBackgroundColor(Color.rgb(r, g, b));
}
});
if(mPreviewType == PreviewType.GB) {
outputFrame.convertTo(outputFrame, CvType.CV_64FC3);
return getGBColor(rgb2hsi(outputFrame));
} else if (mPreviewType == PreviewType.HSI) {
outputFrame.convertTo(outputFrame, CvType.CV_64FC3);
return rgb2hsi(outputFrame);
} else {
return outputFrame;
}
}
My MainActivity implements CameraBridgeViewBase.CvCameraViewListener2
[Edit]
I think that the reason why it return an array of size 4 is because the frame is in RGBA format, not RGB format.
Therefore, how to convert RGBA to HSI and preview the frame on the screen?
The problem here is that your hsiFrame is a 4 channel image and your hsi array has only 3 values. You need to add one term corresponding to alpha channel to your hsi array. Making either of the following changes should work for you:
1. double[] hsi = {colorH, colorS, colorI, rgb[3]};
2. Mat hsiFrame = new Mat(rgbFrame.size(), CvType.CV_8UC3);
Hope this helps.
I have implemented ellipsize text function that works perfectly correct for English Strings. When applied to some languages as Deutch however strings are not truncated properly. After debuugging I came to conclusion that measureText is not workning properly. Here is my code:
for (int i = 0; i < valueCount; i++) {
textWidth = mSelectorWheelPaint.measureText(mOriginalValues[i]);
if (textWidth > maxStringWidth) {
float diff = textWidth - maxStringWidth;
int numlettersToSubstract = Math.round(diff / oneLetterWidth);
int length = mOriginalValues[i].length();
mDisplayedValues[i] = mOriginalValues[i].substring(0,
(length - 1) - (numlettersToSubstract + 3));
if (!CustomLocaleUtil.isRTL()) {
mDisplayedValues[i] = String.format("%s...",
mDisplayedValues[i]);
} else {
mDisplayedValues[i] = String.format("...%s",
mDisplayedValues[i]);
}
} else {
mDisplayedValues[i] = mOriginalValues[i];
}
I want to get the dominant color in an Android CvCameraViewFrame object. I use the following OpenCV Android code to do that. This code is converted from OpenCV c++ code to OpenCV Android code. In the following code I loop through all the pixels in my camera frame and find the color of each pixel and store them in a HashMap to find the dominant color at the end of the loop. To loop through each pixel it takes about 30 seconds. This is unacceptable for me. Could somebody please review this code and point me how can I find the dominant color in a camera frame.
private String[] colors = {"cBLACK", "cWHITE", "cGREY", "cRED", "cORANGE", "cYELLOW", "cGREEN", "cAQUA", "cBLUE", "cPURPLE", "cPINK", "cRED"};
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
mRgba = inputFrame.rgba();
if (mIsColorSelected) {
Imgproc.cvtColor(mRgba, mRgba, Imgproc.COLOR_BGR2HSV);
int h = mRgba.height(); // Pixel height
int w = mRgba.width(); // Pixel width
int rowSize = (int)mRgba.step1(); // Size of row in bytes, including extra padding
float initialConfidence = 1.0f;
Map<String, Integer> tallyColors = new HashMap<String, Integer>();
byte[] pixelsTotal = new byte[h*rowSize];
mRgba.get(0,0,pixelsTotal);
//This for loop takes about 30 seconds to process for my camera frame
for (int y=0; y<h; y++) {
for (int x=0; x<w; x++) {
// Get the HSV pixel components
int hVal = (int)pixelsTotal[(y*rowSize) + x + 0]; // Hue
int sVal = (int)pixelsTotal[(y*rowSize) + x + 1]; // Saturation
int vVal = (int)pixelsTotal[(y*rowSize) + x + 2]; // Value (Brightness)
// Determine what type of color the HSV pixel is.
String ctype = getPixelColorType(hVal, sVal, vVal);
// Keep count of these colors.
int totalNum = 0;
try{
totalNum = tallyColors.get(ctype);
} catch(Exception ex){
totalNum = 0;
}
totalNum++;
tallyColors.put(ctype, totalNum);
}
}
int tallyMaxIndex = 0;
int tallyMaxCount = -1;
int pixels = w * h;
for (int i=0; i<colors.length; i++) {
String v = colors[i];
int pixCount;
try{
pixCount = tallyColors.get(v);
} catch(Exception e){
pixCount = 0;
}
Log.i(TAG, v + " - " + (pixCount*100/pixels) + "%, ");
if (pixCount > tallyMaxCount) {
tallyMaxCount = pixCount;
tallyMaxIndex = i;
}
}
float percentage = initialConfidence * (tallyMaxCount * 100 / pixels);
Log.i(TAG, "Color of currency note: " + colors[tallyMaxIndex] + " (" + percentage + "% confidence).");
}
return mRgba;
}
private String getPixelColorType(int H, int S, int V)
{
String color;
if (V < 75)
color = "cBLACK";
else if (V > 190 && S < 27)
color = "cWHITE";
else if (S < 53 && V < 185)
color = "cGREY";
else { // Is a color
if (H < 14)
color = "cRED";
else if (H < 25)
color = "cORANGE";
else if (H < 34)
color = "cYELLOW";
else if (H < 73)
color = "cGREEN";
else if (H < 102)
color = "cAQUA";
else if (H < 127)
color = "cBLUE";
else if (H < 149)
color = "cPURPLE";
else if (H < 175)
color = "cPINK";
else // full circle
color = "cRED"; // back to Red
}
return color;
}
Thank you very much.
OpenCV has an Histogram method which counts all image colors. After the histogram is calculated all you would have to do is to chose the one with the biggest count...
Check here for a tutorial (C++): Histogram Calculation.
You might also the this stackoverflow answer which shows an example on how to use Android's histogram function Imgproc.calcHist().
Think about to resize your images, then you may multiply the results by the same scale:
resize( larg_image, smallerImage , interpolation=cv.CV_INTER_CUBIC );
Or,
you may check these solutions:
You could find dominant color using k-mean clustering method.
this link will be useful.
https://www.youtube.com/watch?v=f54-x3PckH8
in Android I would like to draw a PieChart with a dynamically number of pies. Each pie should have a different color from a gradient.
For example I would like to have a gradient from light brown to dark brown. If I need to draw five pies, I need five volors from the start to the end of this gradient.
How can I do that in Java with the Android framework?
I found out that I can create a LinearGradient for a line, i.e.:
LinearGradient lg = new LinearGradient(1, 1, 5, 5, toRGB("lightbrown"), toRGB("darkbrown"), TileMode.REPEAT);
But I did not found any function to get a color from this line, i.e.:
// for the five needed RGB colors from the gradient line
lg.getRGBColor(1, 1);
lg.getRGBColor(2, 2);
lg.getRGBColor(3, 3);
lg.getRGBColor(4, 4);
lg.getRGBColor(5, 5);
Do you have any ideas how I can get this?
Thanks!
You cannot get these values directly from the LinearGradient. The gradient doesn't contain the actual drawing. To get these values, you can paint them to a canvas and pull the colors out of the canvas, or what I'd suggest would be to calculate the values yourself.
It's a repeating linear gradient in five steps and you have the RGB values for the first and last color. The rest is just math. Here's the pseudo code:
int r1 = startColor.red;
int g1 = startColor.green;
int b1 = startColor.blue;
int r2 = endColor.red;
int g2 = endColor.green;
int b2 = endColor.blue;
int redStep = r2 - r1 / 4;
int greenStep = g2 - g1 / 4;
int blueStep = b2 - b1 / 4;
firstColor = new Color(r1, g1, b1);
secondColor = new Color(r1 + redStep, g1 + greenStep, b1 + blueStep);
thirdColor = new Color(r1 + redStep * 2, g1 + greenStep * 2, b1 + blueStep * 2);
fourthColor = new Color(r1 + redStep * 3, g1 + greenStep * 3, b1 + blueStep * 3);
fifthColor = new Color(r1 + redStep * 4, g1 + greenStep * 4, b1 + blueStep * 4);
Another approach that is a little more reusable (I seem to bump into this problem all the time). It's a bit more code. Here is the usage:
int[] colors = {toRGB("lightbrown"), toRGB("darkbrown")};//assuming toRGB : String -> Int
float[] positions = {1, 5};
getColorFromGradient( colors, positions, 1 )
//...
getColorFromGradient( colors, positions, 5 )
Supporting functions
public static int getColorFromGradient(int[] colors, float[] positions, float v ){
if( colors.length == 0 || colors.length != positions.length ){
throw new IllegalArgumentException();
}
if( colors.length == 1 ){
return colors[0];
}
if( v <= positions[0]) {
return colors[0];
}
if( v >= positions[positions.length-1]) {
return colors[positions.length-1];
}
for( int i = 1; i < positions.length; ++i ){
if( v <= positions[i] ){
float t = (v - positions[i-1]) / (positions[i] - positions[i-1]);
return lerpColor(colors[i-1], colors[i], t);
}
}
//should never make it here
throw new RuntimeException();
}
public static int lerpColor( int colorA, int colorB, float t){
int alpha = (int)Math.floor(Color.alpha(colorA) * ( 1 - t ) + Color.alpha(colorB) * t);
int red = (int)Math.floor(Color.red(colorA) * ( 1 - t ) + Color.red(colorB) * t);
int green = (int)Math.floor(Color.green(colorA) * ( 1 - t ) + Color.green(colorB) * t);
int blue = (int)Math.floor(Color.blue(colorA) * ( 1 - t ) + Color.blue(colorB) * t);
return Color.argb(alpha, red, green, blue);
}
I have wrote the util class for calculate colors gradient.
via very, very simple Kotlin code:
val pink = Colar(245, 9, 253)
val lime = Colar(0, 253, 32)
lp_1.colors = (pink toColor lime).run {
gradient { 0 upTo 3 }
}
lp_2.colors = (pink toColor lime).run {
gradient { 0 upTo 9 }
}
lp_3.colors = (pink toColor lime).run {
gradient { 3 upTo 9}
}
Supporting util class
class StepGradientUtil(private var colar1: Colar?, private var colar2: Colar?) {
private var mSteps: Int = 0
infix fun StepGradientUtil.gradient(f: () -> IntRange): IntArray {
val result = f.invoke().map {
it.colorStep()
}.toIntArray()
recycler()
return result
}
infix fun Int.upTo(steps: Int): IntRange {
mSteps = steps
return (this until steps)
}
private fun recycler() {
mSteps = 0
colar1 = null
colar2 = null
}
private fun Int.colorStep() = Color.rgb(
(colar1!!.r * (mSteps - this) + colar2!!.r * this) / mSteps,
(colar1!!.g * (mSteps - this) + colar2!!.g * this) / mSteps,
(colar1!!.b * (mSteps - this) + colar2!!.b * this) / mSteps
)
}
data class Colar(
val r: Int,
val g: Int,
val b: Int
)
infix fun Colar.toColor(colar: Colar) = StepGradientUtil(colar1 = this, colar2 = colar)
See full source code sample on repo
Was just wondering if you could use the shift operator in android I am getting a syntax error when trying it. the operator is >> << >>> . If it doesn't support it is their an android sdk equivalent?
EDIT: here is the code i am using. I am trying to do a per pixel collision detection and was trying this out.
public void getBitmapData(Bitmap bitmap1, Bitmap bitmap2){
int[] bitmap1Pixels;
int[] bitmap2Pixels;
int bitmap1Height = bitmap1.getHeight();
int bitmap1Width = bitmap1.getWidth();
int bitmap2Height = bitmap1.getHeight();
int bitmap2Width = bitmap1.getWidth();
bitmap1Pixels = new int[bitmap1Height * bitmap1Width];
bitmap2Pixels = new int[bitmap2Height * bitmap2Width];
bitmap1.getPixels(bitmap1Pixels, 0, bitmap1Width, 1, 1, bitmap1Width - 1, bitmap1Height - 1);
bitmap2.getPixels(bitmap2Pixels, 0, bitmap2Width, 1, 1, bitmap2Width - 1, bitmap2Height - 1);
// Find the first line where the two sprites might overlap
int linePlayer, lineEnemy;
if (ninja.getY() <= enemy.getY()) {
linePlayer = enemy.getY() - ninja.getY();
lineEnemy = 0;
} else {
linePlayer = 0;
lineEnemy = ninja.getY() - enemy.getY();
}
int line = Math.max(linePlayer, lineEnemy);
// Get the shift between the two
int x = ninja.getX() - enemy.getX();
int maxLines = Math.max(bitmap1Height, bitmap2Height);
for (; line <= maxLines; line ++) {
// if width > 32, then you need a second loop here
long playerMask = bitmap1Pixels[linePlayer];
long enemyMask = bitmap2Pixels[lineEnemy];
// Reproduce the shift between the two sprites
if (x < 0) playerMask << (-x);
else enemyMask << x;
// If the two masks have common bits, binary AND will return != 0
if ((playerMask & enemyMask) != 0) {
// Contact!
Log.d("pixel collsion","we have pixel on pixel");
}
}
}
If you're appending to a string you'll get an error unless you put the arithmetic operations in parentheses:
jcomeau#intrepid:/tmp$ cat test.java
public class test {
public static void main(String args[]) {
int test = 42;
System.out.println("" + (test >> 1) + ", " + (test << 1) + ", " + (test >>> 1));
}
}
jcomeau#intrepid:/tmp$ java test
21, 84, 21
Java, which is used by Android does support bitwise operations. Here's a handy guide.