OpenGL es 2.0 and 16 bit channel - android

It possible to return 16 bit value from fragment shader on Android devices?
I've made this conversion to convert 16 bit "x" value to 4444:
vec4 convertToVec4(float x)
{
int iX = int(65535.0 * x);
int r = (iX / (0x1000));
int g = (iX / (0x100)) - r*0x10;
int b = (iX / (0x10)) - (r*0x100 + g*0x10);
int a = (iX) - (r*0x1000 + g*0x100 + b*0x10);
return vec4(float(r)/15.0, float(g)/15.0, float(b)/15.0, float(a)/15.0);
}
and to get back 16 bit "x" from 4444:
float getFloat(vec4 v)
{
vec4 col = v * 15.0;
int sum = int(col.r*4096.0) + int(col.g*256.0) + int(col.b*16.0) + int(col.a);
return float(sum) / 65535.0;
}
It works fine but it is very slow. Is there some way to pass 16 bit color in one channel (eg. red or alpha)?

Two functions are in Shader ? If so you had better calculate that in cpu with neon and then gives the returned value to GLSL
Why don't you use 32 bit channel instead of 16 bit ?

Related

Incorrect frequency on manually drawn graph

I am currently writting a a spectrum analyzer for android for university and part of this involves plotting the FFT of sound. However, I am having an issue with plotting the frequencies. The freq values start off correct, but as i move to higher frequencies the error is becoming greater and greater (at 3000Hz, the graph will show ~3750). I feel as though there is an error in the way I am calculating the x-axis or freq values. This is a manually drawn graph for speed purposes.
If more info/code is needed just let me know, but my guess is that it is something simple that I have overlooked. Thanks
xVal is the frequency value. and the scale value is to scale it according to the real graph dimensions.
int length = currentWaveDataDouble.length;
int pow2 = Integer.highestOneBit(length) << 1;
int sampleRate = 44100;
...
//actual plot part
for(int i =0; i<p2.length; i++) {
float xVal = (float)(i * scaleX.ScaleValue(((double) sampleRate / (pow2 >> 1))));
if (xVal < maxFreqPlus1) {
xVal += axisWidth + yAxisMargin;
float yVal = (float) scaleY.ScaleValue(p2[i]);
yVal += axisWidth + xAxisMargin;
canvas.drawPoint(xVal,yVal, marker);
if(yVal > yMax)
{
yMax = yVal;
xMax = xVal;
}
}
}
Freq generator set to 4000 Hz
Freq generator set to 1000 Hz (value is 1250Hz)
Found the issue. it was in the scaler.
ValueScaler scaleY = new ValueScaler(0,maxAmpPlus1 - yAxisMargin,0,baseY);
ValueScaler scaleX = new ValueScaler(0,maxFreqPlus1 - xAxisMargin,0,baseX);
i wasn't taking into account the x and y margin when scaling the numbers.

RenderScript wrongly manipulating output of kernel

I'm trying to use Android's RenderScript to render a semi-transparent circle behind an image, but things go very wrong when returning a value from the RenderScript kernel.
This is my kernel:
#pragma version(1)
#pragma rs java_package_name(be.abyx.aurora)
// We don't need very high precision floating points
#pragma rs_fp_relaxed
// Center position of the circle
int centerX = 0;
int centerY = 0;
// Radius of the circle
int radius = 0;
// Destination colour of the background can be set here.
float destinationR;
float destinationG;
float destinationB;
float destinationA;
static int square(int input) {
return input * input;
}
uchar4 RS_KERNEL circleRender(uchar4 in, uint32_t x, uint32_t y) {
//Convert input uchar4 to float4
float4 f4 = rsUnpackColor8888(in);
// Check if the current coordinates fall inside the circle
if (square(x - centerX) + square(y - centerY) < square(radius)) {
// Check if current position is transparent, we then need to add the background!)
if (f4.a == 0) {
uchar4 temp = rsPackColorTo8888(0.686f, 0.686f, 0.686f, 0.561f);
return temp;
}
}
return rsPackColorTo8888(f4);
}
Now, the rsPackColorTo8888() function takes 4 floats with a value between 0.0 and 1.0. The resulting ARGB-color is then found by calculating 255 times each float value. So the given floats correspond to the color R = 0.686 * 255 = 175, G = 0.686 * 255 = 175, B = 0.686 * 255 = 175 and A = 0.561 * 255 = 143.
The rsPackColorTo8888() function itself works correctly, but when the found uchar4 value is returned from the kernel, something really weird happens. The R, G and B value changes to respectively Red * Alpha = 56, Green * Alpha = 56 and Blue * Alpha = 56 where Alpha is 0.561. This means that no value of R, G and B can ever be larger than A = 0.561 * 255.
Setting the output manually, instead of using rsPackColorTo8888() yields exact the same behavior. I mean that following code produces the exact same result, which in turn proofs that rsPackColorTo8888() is not the problem:
if (square(x - centerX) + square(y - centerY) < square(radius)) {
// Check if current position is transparent, we then need to add the background!)
if (f4.a == 0) {
uchar4 temp;
temp[0] = 175;
temp[1] = 175;
temp[2] = 175;
temp[3] = 143;
return temp;
}
}
This is the Java-code from which the script is called:
#Override
public Bitmap renderParallel(Bitmap input, int backgroundColour, int padding) {
ResizeUtility resizeUtility = new ResizeUtility();
// We want to end up with a square Bitmap with some padding applied to it, so we use the
// the length of the largest dimension (width or height) as the width of our square.
int dimension = resizeUtility.getLargestDimension(input.getWidth(), input.getHeight()) + 2 * padding;
Bitmap output = resizeUtility.createSquareBitmapWithPadding(input, padding);
output.setHasAlpha(true);
RenderScript rs = RenderScript.create(this.context);
Allocation inputAlloc = Allocation.createFromBitmap(rs, output);
Type t = inputAlloc.getType();
Allocation outputAlloc = Allocation.createTyped(rs, t);
ScriptC_circle_render circleRenderer = new ScriptC_circle_render(rs);
circleRenderer.set_centerX(dimension / 2);
circleRenderer.set_centerY(dimension / 2);
circleRenderer.set_radius(dimension / 2);
circleRenderer.set_destinationA(((float) Color.alpha(backgroundColour)) / 255.0f);
circleRenderer.set_destinationR(((float) Color.red(backgroundColour)) / 255.0f);
circleRenderer.set_destinationG(((float) Color.green(backgroundColour)) / 255.0f);
circleRenderer.set_destinationB(((float) Color.blue(backgroundColour)) / 255.0f);
circleRenderer.forEach_circleRender(inputAlloc, outputAlloc);
outputAlloc.copyTo(output);
inputAlloc.destroy();
outputAlloc.destroy();
circleRenderer.destroy();
rs.destroy();
return output;
}
When alpha is set to 255 (or 1.0 as a float), the returned color-values (inside my application's Java-code) are correct.
Am I doing something wrong, or is this really a bug somewhere in the RenderScript-implementation?
Note: I've checked and verified this behavior on a Oneplus 3T (Android 7.1.1), a Nexus 5 (Android 7.1.2), Android-emulator version 7.1.2 and 6.0
Instead of passing the values with the type:
uchar4 temp = rsPackColorTo8888(0.686f, 0.686f, 0.686f, 0.561f);
Trying creating a float4 and passing that.
float4 newFloat4 = { 0.686, 0.686, 0.686, 0.561 };
uchar4 temp = rsPackColorTo8888(newFloat4);

Converting height in centimeters to inches but cant get decimal points

Trying to convert centimeters to inches then round to the nearest half inch and print 1 decimal point.
3.1 = 3.0
3.2 = 3.0
3.3 = 3.5
3.6 = 3.5
3.8 = 4.0
float index;
float height;
index = (Math.round((height * .393701)*2))/2;
text.setText("Index: " + index);
When I print index it wont show the decimal. Once the number reaches .75 or higher it rounds to the next higher whole number.
Try this,
float index;
float height;
index = (float) (Math.round((height / 2.54) * 10) / 10.0);
text.setText("Index: " + index);
I put an f at the end of each number.
(Math.round((height / 2.54f)*2f)/2f)
Height
147 / 2.54 = 57.874016 = 58
148 / 2.54 = 58.26772 = 58.5

OpenGLES: How to get the lower 8 bits of an int

I'm trying to split a 16 bit integer into two unsigned bytes and then recombine them inside a shader to use as a position. However, OpenGLES reserves & and <<, so I've had to come up with a couple of tricks in order to retrieve each section.
precision highp float;
uniform sampler2D u_Positions;
uniform sampler2D u_Velocities;
// uniform vec2 vpSize;
void main() {
vec2 coord = gl_FragCoord.xy / vec2(256.0, 256.0);
vec4 position = texture2D(u_Positions, coord);
vec4 velocity = texture2D(u_Velocities, coord);
vec2 real_Position = vec2((position.r * 256.0 * 256.0) + (position.g * 256.0),(position.b * 256.0 * 256.0) + (position.a * 256.0));
real_Position.x += ((velocity.r * 256.0) + velocity.g) * 256.0;
real_Position.y += ((velocity.b * 256.0) + velocity.a) * 256.0;
vec4 color = vec4(1.0);
color.r = float(int(real_Position.x) / 256) / 256.0;
color.g = mod(real_Position.x, 256.0) / 256.0;
color.b = float(int(real_Position.y) / 256) / 256.0;
color.a = 1.0;
gl_FragColor = color;
}
So I take the red component multiply by 256 to put it in the range of 1-256 and then shift it to left 8 bits by multiplying by 256. Then I do the same for the lower half without shifting then add them together.
The problem I have however, is converting back into the [0,1] range. The top half im just shifting to the right 8 bits and then dividing by 256. However for the bottom half since there is no and I use modulo 256 to get the lower 8. But for some reason I end up with random 1's attached to it.
So I want to use 960 for my x component I would end up with 3 red 195 green instead of 192 green. If I want to use 0 I end up with my position being 1. The lower 8 bits off. What am I doing wrong with this?
i think you'd have to multiply with 255 instead of 256 (in some cases). why? because if you multiply a [0,1] value range with 256 you get a [0,256] value range -> 257 individual values -> overflow (largest 0-based 8bit number is 255)
EDIT: seems like a classical 1-off error. if you divide a [0,256] range through 256, you get a [1/256,1] range. eg.
color.r = float(int(real_Position.x) / 256) / 255.0;
should be the correct calculation in this case. also, if you multiply a [0,1] value with 256 and assign this value to a byte, the result would be the same until you actually hit 1, where the byte would be 0 again. why that? (254/255) * 256 = 254.99 = 254 (because of int rounding), but 1*256=256 (byte range is [0,255]). so everywhere you are converting between color [0,1] floats and bytes, you have to multiply/divide with 255 while everywhere you convert between 8-bit and 16-bit values you have to multiply/divide with 256.
in general equations:
lb=w%256 //lower byte from 16bit
hb=w/256 //higher byte from 16bit
w=lb+hb*256 //16bit from bytes
b=f*255 //byte from [0,1] float (0->0, 1->255)
f=b/255.0 //[0,1] float from byte (0->0, 255->1)

Color Image in the Google Tango Leibniz API

I am trying to capture the image data in the onFrameAvailable method from a Google Tango. I am using the Leibniz release. In the header file it is said that the buffer contains HAL_PIXEL_FORMAT_YV12 pixel data. In the release notes they say the buffer contains YUV420SP. But in the documentation it is said the pixels are RGBA8888 format (). I am a little confused and additionally. I don't really get image data but a lot of magenta and green. Right now I am trying to convert from YUV to RGB similar to this one. I guess there is something wrong with the stride, too. Here eís the code of the onFrameAvailable method:
int size = (int)(buffer->width * buffer->height);
for (int i = 0; i < buffer->height; ++i)
{
for (int j = 0; j < buffer->width; ++j)
{
float y = buffer->data[i * buffer->stride + j];
float v = buffer->data[(i / 2) * (buffer->stride / 2) + (j / 2) + size];
float u = buffer->data[(i / 2) * (buffer->stride / 2) + (j / 2) + size + (size / 4)];
const float Umax = 0.436f;
const float Vmax = 0.615f;
y = y / 255.0f;
u = (u / 255.0f - 0.5f) ;
v = (v / 255.0f - 0.5f) ;
TangoData::GetInstance().color_buffer[3*(i*width+j)]=y;
TangoData::GetInstance().color_buffer[3*(i*width+j)+1]=u;
TangoData::GetInstance().color_buffer[3*(i*width+j)+2]=v;
}
}
I am doing the yuv to rgb conversion in the fragment shader.
Has anyone ever obtained an RGB image for the Google Tango Leibniz release? Or had someone similar problems when converting from YUV to RGB?
YUV420SP (aka NV21) is correct for the time being. An explanation is here. In this format you have a width x height array where each element is a Y byte, followed by a width/2 x height/2 array where each element is a V byte and a U byte. Your code is implementing YV21, which has separate arrays for V and U instead of interleaving them in one array.
You mention that you are doing YUV to RGB conversion in a fragment shader. If all you want to do with the camera images is draw then you can use TangoService_connectTextureId() and TangoService_updateTexture() instead of TangoService_connectOnFrameAvailable(). This approach delivers the camera image to you already in an OpenGL texture that gives your fragment shader RGB values without bothering with the pixel format details. You will need to bind to GL_TEXTURE_EXTERNAL_OES (instead of GL_TEXTURE_2D), and your fragment shader would look something like this:
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec4 v_t;
uniform samplerExternalOES colorTexture;
void main() {
gl_FragColor = texture2D(colorTexture, v_t.xy);
}
If you really do want to pass YUV data to a fragment shader for some reason, you can do so without preprocessing it into floats. In fact, you don't need to unpack it at all - for NV21 just define a 1-byte texture for Y and a 2-byte texture for VU, and load the data as-is. Your fragment shader will use the same texture coordinates for both.
By the way, if someone experienced problems with capturing the image data on the Leibniz release, too: One of the developers told me that there is a bug concerning the camera and that it should be fixed with the Nash release.
The bug caused my buffer to be null but when I used the Nash update I got data again. However, right now the problem is that the data I am using doesn't make sense. I guess/hope the cause is that the Tablet didn't get the OTA update yet (there can be a gap between the actual release date and the OTA software update).
Just try code following:
//C#
public bool YV12ToPhoto(byte[] data, int width, int height, out Texture2D photo)
{
photo = new Texture2D(width, height);
int uv_buffer_offset = width * height;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
int x_index = j;
if (j % 2 != 0)
{
x_index = j - 1;
}
// Get the YUV color for this pixel.
int yValue = data[(i * width) + j];
int uValue = data[uv_buffer_offset + ((i / 2) * width) + x_index + 1];
int vValue = data[uv_buffer_offset + ((i / 2) * width) + x_index];
// Convert the YUV value to RGB.
float r = yValue + (1.370705f * (vValue - 128));
float g = yValue - (0.689001f * (vValue - 128)) - (0.337633f * (uValue - 128));
float b = yValue + (1.732446f * (uValue - 128));
Color co = new Color();
co.b = b < 0 ? 0 : (b > 255 ? 1 : b / 255.0f);
co.g = g < 0 ? 0 : (g > 255 ? 1 : g / 255.0f);
co.r = r < 0 ? 0 : (r > 255 ? 1 : r / 255.0f);
co.a = 1.0f;
photo.SetPixel(width - j - 1, height - i - 1, co);
}
}
return true;
}
I have succeeded.

Categories

Resources