I am trying to create a wallpaper and am using the HSV conversion in the "android.graphics.color" class. I was very surprised when i realized that a conversion of a created HSV color with a specified hue (0..360) to a rgb color (an integer) and a back conversion to a HSV color will not result in the same hue. This is my code:
int c = Color.HSVToColor(new float[] { 100f, 1, 1 });
float[] f = new float[3];
Color.colorToHSV(c, f);
alert(f[0]);
I am starting with a hue of 100 degree and the result is 99.76471.
I wonder why there is that (in my opinion) relatively big inaccuracy.
But a much bigger problem is, that when you put that value in the code again, the new result decreases again.
int c = Color.HSVToColor(new float[] { 99.76471f, 1, 1 });
float[] f = new float[3];
Color.colorToHSV(c, f);
alert(f[0]);
If I start with 99.76471, I get 99.52941. This is kind of a problem for me.
I did something similar in java with the "java.awt.Color" class where I did not have those problems. Unfortunately, I cannot use this class in android.
This is an interesting problem. It's not avoidable with the android class because of low float precision. However, I found a similar solution written in javascript here.
If it's important enough for you to want to define your own method/class to do the conversions, here is a Java conversion which should give you better precision:
#Size(3)
/** Does the same as {#link android.graphics.Color#colorToHSV(int, float[])} */
public double[] colorToHSV(#ColorInt int color) {
//this line copied vertabim
return rgbToHsv((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF);
}
#Size(3)
public double[] rgbToHsv(double r, double g, double b) {
final double max = Math.max(r, Math.max(g, b));
final double min = Math.min(r, Math.min(g, b));
final double diff = max - min;
final double h;
final double s = ((max == 0d)? 0d : diff / max);
final double v = max / 255d;
if (min == max) {
h = 0d;
} else if (r == max) {
double tempH = (g - b) + diff * (g < b ? 6: 0);
tempH /= 6 * diff;
h = tempH;
} else if (g == max) {
double tempH = (b - r) + diff * 2;
tempH /= 6 * diff;
h = tempH;
} else {
double tempH = (r - g) + diff * 4;
tempH /= 6 * diff;
h = tempH;
}
return new double[] { h, s, v };
}
I have to confess ignorance here - I've done quick conversion and not had time to test properly. There might be a more optimal solution, but this should get you started at least.
Don't miss the mirrored procedure from the source link. Next is the translation to the Kotlin lang.
fun hsvToRGB(hsv: DoubleArray): Int {
val i = floor(hsv[0] * 6).toInt()
val f = hsv[0] * 6 - i
val p = hsv[2] * (1 - hsv[1])
val q = hsv[2] * (1 - f * hsv[1])
val t = hsv[2] * (1 - (1 - f) * hsv[1])
val r: Double
val g: Double
val b: Double
when (i % 6) {
0 -> {r = hsv[2]; g = t; b = p}
1 -> {r = q; g = hsv[2]; b = p}
2 -> {r = p; g = hsv[2]; b = t}
3 -> {r = p; g = q; b = hsv[2]}
4 -> {r = t; g = p; b = hsv[2]}
5 -> {r = hsv[2]; g = p; b = q}
else -> {r = 0.0; g = 0.0; b = 0.0}
}
return Color.rgb((r * 255).roundToInt(), (g * 255).roundToInt(), (b * 255).roundToInt())
}
fun rgbToHSV(color: Int, target: DoubleArray) {
val r = Color.red(color).toDouble()
val g = Color.green(color).toDouble()
val b = Color.blue(color).toDouble()
val max = kotlin.math.max(r, kotlin.math.max(g, b))
val min = kotlin.math.min(r, kotlin.math.min(g, b))
val diff = max - min
target[1] = if (max == 0.0) {0.0} else {diff / max}
target[2] = max / 255.0
target[0] = if (min == max) {
0.0
} else if (r == max) {
var tempH = (g - b) + diff * if (g < b) { 6} else {0}
tempH /= 6 * diff
tempH
} else if (g == max) {
var tempH = (b - r) + diff * 2
tempH /= 6 * diff
tempH
} else {
var tempH = (r - g) + diff * 4
tempH /= 6 * diff
tempH
}
}
Related
I trained a model using tensorflow and then converted it to tensorflow-lite format.
Model inference worked absolutely fine on laptop using python.
Then I put the model in Android app and used tensorflowlite interpreter for inference and result was nothing but a full black image.
I ported the code in python to Java as is, still getting this garbage result.
Any idea where I might be going wrong here.
Python Code:
def preprocess(img):
return (img / 255. - 0.5) * 2
def deprocess(img):
return (img + 1) / 2
img_size = 256
frozen_model_filename = os.path.join('model/tflite', 'model.tflite')
image_1 = cv2.resize(imread(image_1), (img_size, img_size))
X_1 = np.expand_dims(preprocess(image_1), 0)
X_1 = X_1.astype(np.float32)
image_2 = cv2.resize(imread(image_2), (img_size, img_size))
X_2 = np.expand_dims(preprocess(image_2), 0)
X_2 = X_2.astype(np.float32)
interpreter = tf.lite.Interpreter(model_path=frozen_model_filename)
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
interpreter.set_tensor(input_details[0]['index'], X_1)
interpreter.set_tensor(input_details[1]['index'], X_2)
interpreter.invoke()
Output = interpreter.get_tensor(output_details[0]['index'])
Output = deprocess(Output)
imsave('result_tflite.jpg', Output[0])
Corresponding Java code for Android platform:
private ByteBuffer convertBitmapToByteBuffer(Bitmap bitmap) {
Bitmap resized = Bitmap.createScaledBitmap(bitmap, IMAGE_SIZE, IMAGE_SIZE, false);
ByteBuffer byteBuffer;
if(isQuant) {
byteBuffer = ByteBuffer.allocateDirect(BATCH_SIZE * IMAGE_SIZE * IMAGE_SIZE * PIXEL_SIZE);
} else {
byteBuffer = ByteBuffer.allocateDirect(4 * BATCH_SIZE * IMAGE_SIZE * IMAGE_SIZE * PIXEL_SIZE);
}
byteBuffer.order(ByteOrder.nativeOrder());
int[] intValues = new int[IMAGE_SIZE * IMAGE_SIZE];
resized.getPixels(intValues, 0, resized.getWidth(), 0, 0, resized.getWidth(), resized.getHeight());
int pixel = 0;
byteBuffer.rewind();
for (int i = 0; i < IMAGE_SIZE; ++i) {
for (int j = 0; j < IMAGE_SIZE; ++j) {
final int val = intValues[pixel++];
if(isQuant){
byteBuffer.put((byte) ((val >> 16) & 0xFF));
byteBuffer.put((byte) ((val >> 8) & 0xFF));
byteBuffer.put((byte) (val & 0xFF));
} else {
byteBuffer.putFloat((((val >> 16) & 0xFF) - 0.5f) * 2.0f);
byteBuffer.putFloat((((val >> 8) & 0xFF) - 0.5f) * 2.0f);
byteBuffer.putFloat((((val) & 0xFF ) - 0.5f) * 2.0f);
}
}
}
return byteBuffer;
}
private Bitmap getOutputImage(ByteBuffer output){
output.rewind();
int outputWidth = IMAGE_SIZE;
int outputHeight = IMAGE_SIZE;
Bitmap bitmap = Bitmap.createBitmap(outputWidth, outputHeight, Bitmap.Config.ARGB_8888);
int [] pixels = new int[outputWidth * outputHeight];
for (int i = 0; i < outputWidth * outputHeight; i++) {
int a = 0xFF;
float r = (output.getFloat() + 1) / 2.0f;
float g = (output.getFloat() + 1) / 2.0f;
float b = (output.getFloat() + 1) / 2.0f;
pixels[i] = a << 24 | ((int) r << 16) | ((int) g << 8) | (int) b;
}
bitmap.setPixels(pixels, 0, outputWidth, 0, 0, outputWidth, outputHeight);
return bitmap;
}
private void runInference(){
ByteBuffer byteBufferX1 = convertBitmapToByteBuffer(bitmap_x1);
ByteBuffer byteBufferX2 = convertBitmapToByteBuffer(bitmap_x2);
Object[] inputs = {byteBufferX1, byteBufferX2};
ByteBuffer byteBufferOutput;
if(isQuant) {
byteBufferOutput = ByteBuffer.allocateDirect(BATCH_SIZE * IMAGE_SIZE * IMAGE_SIZE * PIXEL_SIZE);
} else {
byteBufferOutput = ByteBuffer.allocateDirect(4 * BATCH_SIZE * IMAGE_SIZE * IMAGE_SIZE * PIXEL_SIZE);
}
byteBufferOutput.order(ByteOrder.nativeOrder());
byteBufferOutput.rewind();
Map<Integer, Object> outputs = new HashMap<>();
outputs.put(0, byteBufferOutput);
interpreter.runForMultipleInputsOutputs(inputs, outputs);
ByteBuffer out = (ByteBuffer) outputs.get(0);
Bitmap outputBitmap = getOutputImage(out);
// outputBitmap is just a full black image
}
Both Java and Python interpreter are base on C++ implementation so the results should be the same. The error should be in your JAVA code.
Here i think you forgot to multiply and divide to 255.
Im trying to render a video frame using android NDK.
Im using this sample of google Native-Codec NDK sample code and modified it so I can manually display each video frame (non-tunneled).
so I added this code to get the output buffer which is in YUV.
ANativeWindow_setBuffersGeometry(mWindow, bufferWidth, bufferHeight,
WINDOW_FORMAT_RGBA_8888
uint8_t *decodedBuff = AMediaCodec_getOutputBuffer(d->codec, status, &bufSize);
auto format = AMediaCodec_getOutputFormat(d->codec);
LOGV("VOUT: format %s", AMediaFormat_toString(format));
AMediaFormat *myFormat = format;
int32_t w,h;
AMediaFormat_getInt32(myFormat, AMEDIAFORMAT_KEY_HEIGHT, &h);
AMediaFormat_getInt32(myFormat, AMEDIAFORMAT_KEY_WIDTH, &w);
err = ANativeWindow_lock(mWindow, &buffer, nullptr);
and these codes to convert the YUV to RGB and display it using native window.
if (err == 0) {
LOGV("ANativeWindow_lock()");
int width =w;
int height=h;
int const frameSize = width * height;
int *line = reinterpret_cast<int *>(buffer.bits);
for (int y= 0; y < height; y++) {
for (int x = 0; x < width; x++) {
/*accessing YUV420SP elements*/
int indexY = y * width + x;
int indexU = (size + (y / 2) * (width ) + (x / 2) *2);
int indexV = (int) (size + (y / 2) * (width) + (x / 2) * 2 + 1);
/*todo; this conversion to int and then later back to int really isn't required.
There's room for better work here.*/
int Y = 0xFF & decodedBuff[indexY];
int U = 0xFF & decodedBuff[indexU];
int V = 0xFF & decodedBuff[indexV];
/*constants picked up from http://www.fourcc.org/fccyvrgb.php*/
int R = (int) (Y + 1.402f * (V - 128));
int G = (int) (Y - 0.344f * (U - 128) - 0.714f * (V - 128));
int B = (int) (Y + 1.772f * (U - 128));
/*clamping values*/
R = R < 0 ? 0 : R;
G = G < 0 ? 0 : G;
B = B < 0 ? 0 : B;
R = R > 255 ? 255 : R;
G = G > 255 ? 255 : G;
B = B > 255 ? 255 : B;
line[buffer.stride * y + x] = 0xff000000 + (B << 16) + (G << 8) + R;
}
}
ANativeWindow_unlockAndPost(mWindow);
Finally I was able to display a video on my device. Now my problem is the video does not scale to fit the surface view :(
Your thoughts are very much appreciated.
I am trying to get variants of a hex colour, given its percentage of variant. For example my hex colour is #FFFFFF , where i need to find five variants 95% is #F2F2F2 , 85% is #D9D9D9 , 75% is #BFBFBF , 65% is #A6A6A6 , 50% is #808080. How can i calculate this programatically? How can i increase or decrease the brightness of a color code depending on some percent?
Code snippet tried:
void brighten(int color, float fraction) {
int r = Color.red(color);
int g = Color.green(color);
int b = Color.blue(color);
r += (int) (fraction * (255 - r));
g += (int) (fraction * (255 - g));
b += (int) (fraction * (255 - b));
if (r < 0) {
r = 0;
}
if (r > 255) {
r = 255;
}
if (g < 0) {
g = 0;
}
if (g > 255) {
g = 255;
}
if (b < 0) {
b = 0;
}
if (b > 255) {
b = 255;
}
String resultColor = "#" + Integer.toHexString(Color.rgb((int) r, (int) g, (int) b) & 0x00ffffff);
}
This works for colors to some extend as it only darkens with given percentage. But it doesn't works to all color uniformally (example : #000000).
Use the below method to solve your problem.
// Function Calling
brighten(0x00FFFFFF, 65); //Para 1 : color code // Para 2 : percentage of color code that what you want.
void brighten(int color, int persontage) {
double r = Color.red(color);
double g = Color.green(color);
double b = Color.blue(color);
r = Math.ceil((r*persontage) / 100);
g = Math.ceil((g*persontage) / 100);
b = Math.ceil((b*persontage) / 100);
String resultColor = "#" + Integer.toHexString(Color.rgb((int) r, (int) g, (int) b) & 0x00ffffff);
Log.i(TAG, "brighten: resultColor : " + resultColor);
}
I have calculated R,G and B values of an image and do some calculations to get L,a and b values for LAB Colorspace. Now how can I transfer my RGB image into LAB image using these L,a and b Values in Android studio (Except OpenCV's Builtin function because I want to first convert RGB into XYZ and then finally XYZ to LAB color space)?
Pass RGB values to the function of this class which will return an array containing values of LAB
public class CIELab {
String TAG ="RGB";
public double[] rgbToLab(int R, int G, int B) {
double[] lab=new double[3];
double r, g, b, X, Y, Z, xr, yr, zr;
ColorUtils.RGBToLAB(R,G,B,lab);
//Core.absdiff();
// D65/2°
double Xr = 95.047;
double Yr = 100.0;
double Zr = 108.883;
// --------- RGB to XYZ ---------//
r = R/255.0;
g = G/255.0;
b = B/255.0;
if (r > 0.04045)
r = Math.pow((r+0.055)/1.055,2.4);
else
r = r/12.92;
if (g > 0.04045)
g = Math.pow((g+0.055)/1.055,2.4);
else
g = g/12.92;
if (b > 0.04045)
b = Math.pow((b+0.055)/1.055,2.4);
else
b = b/12.92 ;
r*=100;
g*=100;
b*=100;
Log.d(TAG,"R:"+r+" G:"+g+" B:"+b);
X = 0.4124*r + 0.3576*g + 0.1805*b;
Y = 0.2126*r + 0.7152*g + 0.0722*b;
Z = 0.0193*r + 0.1192*g + 0.9505*b;
// --------- XYZ to Lab --------- //
xr = X/Xr;
yr = Y/Yr;
zr = Z/Zr;
if ( xr > 0.008856 )
xr = (float) Math.pow(xr, 1/3.);
else
xr = (float) ((7.787 * xr) + 16 / 116.0);
if ( yr > 0.008856 )
yr = (float) Math.pow(yr, 1/3.);
else
yr = (float) ((7.787 * yr) + 16 / 116.0);
if ( zr > 0.008856 )
zr = (float) Math.pow(zr, 1/3.);
else
zr = (float) ((7.787 * zr) + 16 / 116.0);
double[] lab = new double[3];
lab[0] = (116*yr)-16;
lab[1] = 500*(xr-yr);
lab[2] = 200*(yr-zr);
return lab;
}
}
I have to create a HSV Histogram from a ARGB array using RenderScript in Android. This is the first time i am using RenderScript and i am not sure if i made a mistake because the performance is not so good. Creating a HSV histogram from an 1920x1080 bitmap takes between 100 and 150 ms.
The RenderScript code:
#pragma version(1)
#pragma rs java_package_name(com.test.renderscript)
#pragma rs_fp_relaxed
uchar3 bins;
rs_allocation histogramAllocation;
void __attribute__((kernel)) process(uchar4 in) {
float r = in.r / 255.0;
float g = in.g / 255.0;
float b = in.b / 255.0;
// convert rgb to hsv
float minRGB = min( r, min( g, b ) );
float maxRGB = max( r, max( g, b ) );
float deltaRGB = maxRGB - minRGB;
float h = 0.0;
float s = maxRGB == 0 ? 0 : (maxRGB - minRGB) / maxRGB;
float v = maxRGB;
if (deltaRGB != 0) {
if (r == maxRGB) {
h = (g - b) / deltaRGB;
}
else {
if (g == maxRGB) {
h = 2 + (b - r) / deltaRGB;
}
else {
h = 4 + (r - g) / deltaRGB;
}
}
h *= 60;
if (h < 0) { h += 360; }
if (h == 360) { h = 0; }
}
// quantize hsv
uint qh = h / (360.0 / bins.s0);
uint qs = (s * 100) / (101.0 / bins.s1);
uint qv = (v * 100) / (101.0 / bins.s2);
// calculate bin index and update the count at that index
// (v * bin size H * bin size S) + (s * bin size H) + h;
uint binIndex = (qv * bins.s0 * bins.s1) + (qs * bins.s0) + qh;
uint count = rsGetElementAt_uint(histogramAllocation, binIndex);
rsSetElementAt_uint(histogramAllocation, (count + 1), binIndex);
}
void init() {
uint histogramSize = bins.s0 * bins.s1 * bins.s2;
for (int i=0; i < histogramSize; i++) {
rsSetElementAt_uint(histogramAllocation, 0, i);
}
}
The Kotlin code:
class RsCreateHsvHistogram {
fun execute(rs: RenderScript, src: ByteArray, bins: HsvHistogram.Bins = HsvHistogram.Bins()): HsvHistogram {
val start = SystemClock.elapsedRealtimeNanos()
val histogramSize = bins.h * bins.s * bins.v
// create input allocation
val typeIn = Type.Builder(rs, Element.U8_4(rs))
.setX(src.size / 4)
.create()
val allocIn = Allocation.createTyped(rs, typeIn)
allocIn.copyFrom(src)
// create output allocation -> the histogram allocation
val typeOut = Type.Builder(rs, Element.I32(rs))
.setX(histogramSize)
.create()
val allocOut = Allocation.createTyped(rs, typeOut)
// run the render script
val script = ScriptC_create_hsv_histogram(rs)
script._bins = Short3(bins.h, bins.s, bins.v)
script._histogramAllocation = allocOut
script.forEach_process(allocIn)
// copy output allocation to histogram array
val histogramData = IntArray(histogramSize)
allocOut.copyTo(histogramData)
val stop = SystemClock.elapsedRealtimeNanos()
Timber.e("duration => ${(stop-start) / 1000000.0} ms")
return HsvHistogram(histogramData, bins)
}
}
I hope you can help me improve the performance. Do you think HSV histogram creation can be done in about 20ms? Is this realistic?