Android bilinear image downscaling - android

I am trying to downscale a bitmap using bilinear filtering, but apparently there is something wrong in my code, because the image seems better than nearest neighbour or just using android's downsampling, but not good enough as Image Magick's biliear filter.
Do you see something wrong in my resize method?
private Bitmap resize(Bitmap immutable, int reqWidth, int reqHeight) {
Bitmap bitmap = Bitmap.createBitmap(reqWidth, reqHeight, Config.ARGB_8888);
float scaleFactor = immutable.getHeight() / (float) reqHeight;
for (int x = 1; x < reqWidth - 1; x++) {
for (int y = 1; y < reqHeight - 1; y++) {
float sx = x * scaleFactor;
float sy = y * scaleFactor;
int rx = (int) (x * scaleFactor);
int ry = (int) (y * scaleFactor);
final int tl = immutable.getPixel(rx, ry);
final int tr = immutable.getPixel(rx + 1, ry);
final int bl = immutable.getPixel(rx, ry+1 );
final int br = immutable.getPixel(rx + 1, ry+1);
float xC1 = sx- rx;
float xC2 = 2-xC1;
float yC1 = sy - ry;
float yC2 = 2 - yC1;
xC1/=2;
xC2/=2;
yC1/=2;
yC2/=2;
final float firstAlpha = (Color.alpha(tl) * xC2 + Color.alpha(tr) * xC1);
final float firstRed = (Color.red(tl) * xC2 + Color.red(tr) * xC1);
final float firstBlue = (Color.blue(tl) * xC2 + Color.blue(tr) * xC1);
final float firstGreen = (Color.green(tl) * xC2 + Color.green(tr) * xC1);
final float secondAlpha = (Color.alpha(bl) * xC2 + Color.alpha(br) * xC1);
final float secondRed = (Color.red(bl) * xC2 + Color.red(br) * xC1);
final float secondGreen = (Color.green(bl) * xC2 + Color.green(br) * xC1);
final float secondBlue = (Color.blue(bl) * xC2 + Color.blue(br) * xC1);
int finalColor = Color.argb((int) (yC2 * firstAlpha + yC1 * secondAlpha), (int) (yC2 * firstRed + yC1 * secondRed), (int) (yC2 * firstGreen + yC1
* secondGreen), (int) (yC2 * firstBlue + yC1 * secondBlue));
bitmap.setPixel(x, y, finalColor);
}
}
return bitmap;
}
while Image magick's result is this:

Android uses bilinear downscaling algorithm when BitmapFactory.Options::inSampleSize->BitmapFactory.decodeResource() is called.
So good downscaling algorithm (not nearest neighbor like) consists of just 2 steps (plus calculation of the exact Rect for input/output rectangles cropping):
downscale using BitmapFactory.Options::inSampleSize->BitmapFactory.decodeResource() as close as possible to the resolution that you need but not less than it
get to the exact resolution by downscaling a little bit using Canvas::drawBitmap()
Here is detailed explanation how SonyMobile resolved this task: http://developer.sonymobile.com/2011/06/27/how-to-scale-images-for-your-android-application/
Here is the source code of SonyMobile scale utils: http://developer.sonymobile.com/downloads/code-example-module/image-scaling-code-example-for-android/

Related

How to capture exactly the image inside the rectangular overlay in Camera?

I successfully made the auto crop to some extent for small devices so far. I am facing two issues:
1) the auto crop in big devices lets say 6.5 inches is not working properly
2) i want to take picture inside the rectangle frame
Below is my code:
public static Bitmap crop(Bitmap originalBitmap)
{
double originalWidth = originalBitmap.getWidth();
double originalHeight =
originalBitmap.getHeight();
double scaleX = originalWidth / 1280;
int navBarHeightPxIn1280x720Ui
CommonUtils.px2dp(CommonUtils.get
NavigationBarHeightInPx()) * 5 ;
double scaleXMultiplier = ((double) 1280) /
((double) (1280 - navBarHeightPxIn1280x720Ui));
scaleX = scaleX * scaleXMultiplier;
double scaleY = originalHeight / 720;
int x = (int) (52 * scaleX + 0.5);
int y = (int) (80 * scaleY + 0.5);
int width = (int) (896 * scaleX + 0.5);
int height = (int) (588 * scaleY + 0.5);
return Bitmap.createBitmap(originalBitmap, x, y,
width, height);
}

Why my mesh vertices are moved by the same offset and not by offset calculated by bezier curve

I have a tough one (I think) problem. I am prepearing a game with a part, which is a race. I want to make it similar to oldschool "Lotus" games. Player will see a car from behind, a a road should have turns from time to time. So my idea is to make a Mesh (road), which will have updated vertexes based on bezier curve. A game should check every vertex on the left edge of the mesh and check how much should it be moved to left or right. Same for right edge. Below is my code based on:
http://www.andengine.org/forums/post47301.html?hilit=%20mesh#p47301
Calculations of bezier curve are taken from QuadraticBezierCurveMoveModifier included in andEngine.
private Mesh createRoad(){
//create triangles
//
// E--F
// |\ |
// B--D
// |\ |
// A--C
final int triangleCount = 10;
int i = 0;
final float pInitialX = 0;
float pInintialY = 0;
int vertexCount = Mesh.VERTEX_SIZE * triangleCount * 4;
final float pColor = new Color(0f,0f,0f).getABGRPackedFloat();
final float pBufferData[] = new float[vertexCount];
for(int triangleIndex = 0; triangleIndex < triangleCount; triangleIndex++){
pBufferData[(i * Mesh.VERTEX_SIZE) + Mesh.VERTEX_INDEX_X] = pInitialX;
pBufferData[(i * Mesh.VERTEX_SIZE) + Mesh.VERTEX_INDEX_Y] = pInintialY;
pBufferData[(i * Mesh.VERTEX_SIZE) + Mesh.COLOR_INDEX] = pColor;
pBufferData[((i+1) * Mesh.VERTEX_SIZE) + Mesh.VERTEX_INDEX_X] = pInitialX;
pBufferData[((i+1) * Mesh.VERTEX_SIZE) + Mesh.VERTEX_INDEX_Y] = pInintialY + 30;
pBufferData[((i+1) * Mesh.VERTEX_SIZE) + Mesh.COLOR_INDEX] = pColor;
pBufferData[((i+2) * Mesh.VERTEX_SIZE) + Mesh.VERTEX_INDEX_X] = pInitialX + 200;
pBufferData[((i+2) * Mesh.VERTEX_SIZE) + Mesh.VERTEX_INDEX_Y] = pInintialY;
pBufferData[((i+2) * Mesh.VERTEX_SIZE) + Mesh.COLOR_INDEX] = pColor;
pBufferData[((i+3) * Mesh.VERTEX_SIZE) + Mesh.VERTEX_INDEX_X] = pInitialX + 200;
pBufferData[((i+3) * Mesh.VERTEX_SIZE) + Mesh.VERTEX_INDEX_Y] = pInintialY + 30;
pBufferData[((i+3) * Mesh.VERTEX_SIZE) + Mesh.COLOR_INDEX] = pColor;
i = i + 4;
pInintialY = pInintialY + 30;
}
final VertexBufferObjectManager vbom = engine.getVertexBufferObjectManager();
final HighPerformanceMeshVertexBufferObject pMeshVBOM = new HighPerformanceMeshVertexBufferObject(vbom, pBufferData, pBufferData.length, DrawType.STREAM, true, Mesh.VERTEXBUFFEROBJECTATTRIBUTES_DEFAULT);
road = new Mesh(300, 50, vertexCount, DrawMode.TRIANGLE_STRIP, pMeshVBOM){
#Override
protected void onManagedUpdate(final float pSecondsElapsed) {
super.onManagedUpdate(pSecondsElapsed);
drawByBezier();
onUpdateVertices();
this.mMeshVertexBufferObject.setDirtyOnHardware();
};
void drawByBezier(){
float pBuff[] = pMeshVBOM.getBufferData();
int i = 0;
for (int triangleIndex = 0; triangleIndex < triangleCount; triangleIndex++) {
pBuff[(i * Mesh.VERTEX_SIZE) + Mesh.VERTEX_INDEX_X] = getBezierX(i, 100, 0, 0);
pBuff[((i+1) * Mesh.VERTEX_SIZE) + Mesh.VERTEX_INDEX_X] = getBezierX(i+1, 0, 0, 0);
pBuff[((i+2) * Mesh.VERTEX_SIZE) + Mesh.VERTEX_INDEX_X] = getBezierX(i+2, 0, 0, 0) + 200;
pBuff[((i+3) * Mesh.VERTEX_SIZE) + Mesh.VERTEX_INDEX_X] = getBezierX(i+3, 0, 0, 0) + 200;
i = i + 4;
}
}
float bezierX;
float getBezierX(int triangleIndex, float P1X, float PcX, float P2X){
float t = triangleIndex / (triangleCount * 4);
float u = 1 - t;
float tt = t * t;
float uu = u * u;
float ut2 = 2 * u * t;
return bezierX = (uu * P1X) + (ut2 * PcX) + (tt * P2X);
}
};
return road;
}
Results are:
When I put (i,0,0,0) in all getBezierX() (for all vertices:
https://www.dropbox.com/s/upuyjti66rtm44i/Screenshot_2015-05-07-15-02-17.png?dl=0
When I put (i, 100, 0, 0) for first one getBezierX() the result is:
https://www.dropbox.com/s/w3231zxrwytot71/Screenshot_2015-05-07-15-03-41.png?dl=0
As you can see all first vertexes are moved by the same offset to the right (100). It does not matter what are other parameters, it is always moved by value of the first parameter. Why is that? Do I miss anything here?
EDIT
To clarify. I want to make this black block to be curved like a turn on the road. So its left and right edge should be curved. I wannt to use bezier curve to calculate x position of every vertice of every triangle of a mesh used. y positions should be same as they are. A mesh diagram is commented in the begining of the code. Co I would like to position points A, B , E along one curve , and C, D, F along another moved to the right.
Ok I found a problem but I dont really understand why it was a problem. So in:
float getBezierX(int triangleIndex, float P1X, float PcX, float P2X){
float t = triangleIndex / (triangleCount * 4);
float u = 1 - t;
float tt = t * t;
float uu = u * u;
float ut2 = 2 * u * t;
return bezierX = (uu * P1X) + (ut2 * PcX) + (tt * P2X);
}
I changed float getBezierX(int triangleIndex, float P1X, float PcX, float P2X) to float getBezierX(float triangleIndex, float P1X, float PcX, float P2X)and it works now. But why the int was a problem?

How to fix a memory leak in Android regarding array of arrays?

I've created an application that takes an image captured by the Android device, displays this image in the ImageView. The user can then press a button to either blur or deblur the image. When I run the application on my Android device I can take an image with the camera and display this without any problems. A problem occurs when I press the blur button, which runs some code to blur the image. The application becomes frozen and I get an OutOfMemoryException for a line of my code that creates a new array and stores this in another array in a nested for loop.
This is the code for the nested for loop:
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int xTranslated = (x + width / 2) % width;
int yTranslated = (y + height / 2) % height;
double real = temp[2 * (xTranslated + yTranslated * width)];
double imaginary = temp[2 * (xTranslated + yTranslated * width) + 1];
degradation[2 * (x + y * width)] = real;
degradation[2 * (x + y * width) + 1] = imaginary;
Complex c = new Complex(real, imaginary);
complex[y * width + x] = c;
}
}
This nested for loop deals with data extracted from the input image, which is stored as a Bitmap.
Here is the full method that applies the motion blur:
public Complex[] motionBlur(double[] degradation, int width, int height, double alpha, double gamma, double sigma) {
Complex[] complex = new Complex[width * height];
double[] temp = new double[2 * width * height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
double teta = Math.PI * ( (((x - width/2) % width) * gamma) + ((((y - height/2) % height) * sigma) ));
Sinc sinc = new Sinc();
double real = (Math.cos(teta) * sinc.value(teta)) * alpha;
double imaginary = (Math.sin(teta) * sinc.value(teta)) * alpha;
Complex cConj = new Complex(real, imaginary).conjugate();
temp[2 * (x + y * width)] = cConj.getReal();
temp[2 * (x + y * width) + 1] = cConj.getImaginary();
}
}
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int xTranslated = (x + width / 2) % width;
int yTranslated = (y + height / 2) % height;
double real = temp[2 * (xTranslated + yTranslated * width)];
double imaginary = temp[2 * (xTranslated + yTranslated * width) + 1];
degradation[2 * (x + y * width)] = real;
degradation[2 * (x + y * width) + 1] = imaginary;
Complex c = new Complex(real, imaginary);
complex[y * width + x] = c;
}
}
return complex;
}
Here is a link to what is being output by the logcat when I try to run the application: http://pastebin.com/ysbN9A3s
MainActivity.java:373 corresponds to the line,
Complex c = new Complex(real, imaginary);
Here is nice talk about android crashes.
Pierre-Yves talks about OOM (OutOfMemory error) at the time 11:39. So the problem in OOM is not place where OOM happens. You should profile your app to find the place where most memory is consumed. I should admit that OOM is one of the hardest error to resolve.
Good luck!

Width is shrink after increasing the width & height 2 times openGL C++

I am rendering video on GLSurfaceView by openGL. The openGL portion is written on C++ in native portion. This is my render routine:
void VideoRenderOpenGL2::Render(const unsigned char *pData)
{
......................
// GL_OPERATION is a macro, nothing special
GL_OPERATION(glUseProgram(m_program))
UpdateTextures(pData); // other routine, I will post the function if needed
bool bClear = true;
float vpx = 0.0f;
float vpy = 0.0f;
float vpw = 1.0f;
float vph = 1.0f;
int nOrientation = 0;
float uLeft, uRight, vTop, vBottom;
uLeft = vBottom = 0.0f;
uRight = m_uvx;
vTop = m_uvy;
GLfloat squareUvs[] = {
uLeft, vTop,
uRight, vTop,
uLeft, vBottom,
uRight, vBottom
};
if (bClear) {
GL_OPERATION(glViewport(0, 0, m_nDisplayWidth, m_nDisplayHeight))
GL_OPERATION(glClearColor(0, 0, 0, 1))
GL_OPERATION(glClear(GL_COLOR_BUFFER_BIT))
}
GLfloat squareVertices[8];
// drawing surface dimensions
int screenW = m_nDisplayWidth;
int screenH = m_nDisplayHeight;
if (nOrientation == 90 || nOrientation == 270) {
screenW = m_nDisplayHeight;
screenH = m_nDisplayWidth;
}
int x,y,w,h;
// Fill the smallest dimension, then compute the other one using the image ratio
if (screenW <= screenH) {
float ratio = m_nTextureHeight / (float)m_nTextureWidth;
w = screenW * vpw;
h = w * ratio;
if (h > screenH) {
w *= screenH /(float) h;
h = screenH;
}
x = vpx * m_nDisplayWidth;
y = vpy * m_nDisplayHeight;
} else {
float ratio = m_nTextureWidth / (float)m_nTextureHeight;
h = screenH * vph;
w = h * ratio;
if (w > screenW) {
h *= screenW / (float)w;
w = screenW;
}
x = vpx * screenW;
y = vpy * screenH;
}
// here m_nDisplayWidth = 5536, m_nDisplayHeight = 3114, w = 5536, h = 3114, x = 0, y = 0, screenW = 5536, screenH = 3114, m_nTextureWidth = 1280, m_nTextureHeight = 720
squareVertices[0] = (x - w * 0.5) / screenW - 0.;
squareVertices[1] = (y - h * 0.5) / screenH - 0.;
squareVertices[2] = (x + w * 0.5) / screenW - 0.;
squareVertices[3] = (y - h * 0.5) / screenH - 0.;
squareVertices[4] = (x - w * 0.5) / screenW - 0.;
squareVertices[5] = (y + h * 0.5) / screenH - 0.;
squareVertices[6] = (x + w * 0.5) / screenW - 0.;
squareVertices[7] = (y + h * 0.5) / screenH - 0.;
GL_OPERATION(glViewport(0, 0, m_nDisplayWidth, m_nDisplayHeight))
GLfloat mat[16];
#define VP_SIZE 1.0f
float vpDim = VP_SIZE / (2 * m_scaleFactor);
#define ENSURE_RANGE_A_INSIDE_RANGE_B(a, aSize, bMin, bMax) \
if (2 * aSize >= (bMax - bMin)) \
a = 0; \
else if ((a - aSize < bMin) || (a + aSize > bMax)) { \
float diff; \
if (a - aSize < bMin) diff = bMin - (a - aSize); \
else diff = bMax - (a + aSize); \
a += diff; \
}
float zoom_cx = 0.0f;
float zoom_cy = 0.0f;
ENSURE_RANGE_A_INSIDE_RANGE_B(zoom_cx, vpDim, squareVertices[0], squareVertices[2])
ENSURE_RANGE_A_INSIDE_RANGE_B(zoom_cy, vpDim, squareVertices[1], squareVertices[7])
LoadOrthographicMatrix(
zoom_cx - vpDim,
zoom_cx + vpDim,
zoom_cy - vpDim,
zoom_cy + vpDim,
0, 0.5, mat);
GL_OPERATION(glUniformMatrix4fv(m_uniforms[UNIFORM_PROJ_MATRIX], 1, GL_FALSE, mat))
#define degreesToRadians(d) (2.0 * 3.14157 * d / 360.0)
float rad = degreesToRadians(nOrientation);
GL_OPERATION(glUniform1f(m_uniforms[UNIFORM_ROTATION], rad))
GL_OPERATION(glActiveTexture(GL_TEXTURE0))
GL_OPERATION(glBindTexture(GL_TEXTURE_2D, m_textures[Y]))
GL_OPERATION(glUniform1i(m_uniforms[UNIFORM_TEXTURE_Y], 0))
GL_OPERATION(glActiveTexture(GL_TEXTURE1))
GL_OPERATION(glBindTexture(GL_TEXTURE_2D, m_textures[U]))
GL_OPERATION(glUniform1i(m_uniforms[UNIFORM_TEXTURE_U], 1))
GL_OPERATION(glActiveTexture(GL_TEXTURE2))
GL_OPERATION(glBindTexture(GL_TEXTURE_2D, m_textures[V]))
GL_OPERATION(glUniform1i(m_uniforms[UNIFORM_TEXTURE_V], 2))
GL_OPERATION(glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices))
GL_OPERATION(glEnableVertexAttribArray(ATTRIB_VERTEX))
GL_OPERATION(glVertexAttribPointer(ATTRIB_UV, 2, GL_FLOAT, 1, 0, squareUvs))
GL_OPERATION(glEnableVertexAttribArray(ATTRIB_UV))
GL_OPERATION(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4))
}
And this is LoadOrthographicMatrix:
void VideoRenderOpenGL2::LoadOrthographicMatrix(float left, float right, float bottom, float top, float near, float far, float* mat)
{
float r_l = right - left;
float t_b = top - bottom;
float f_n = far - near;
float tx = - (right + left) / (right - left);
float ty = - (top + bottom) / (top - bottom);
float tz = - (far + near) / (far - near);
mat[0] = (2.0f / r_l);
mat[1] = mat[2] = mat[3] = 0.0f;
mat[4] = 0.0f;
mat[5] = (2.0f / t_b);
mat[6] = mat[7] = 0.0f;
mat[8] = mat[9] = 0.0f;
mat[10] = -2.0f / f_n;
mat[11] = 0.0f;
mat[12] = tx;
mat[13] = ty;
mat[14] = tz;
mat[15] = 1.0f;
}
Suppose, my device dimension is 1080 x 1557 and I am trying to render 2768 x 1557 (height equal to device height and corresponding width keeping aspect ratio with 1280 x 720) sized video on the GLSurfaceView. Everything works fine so far and Render(const unsigned char *pData) is properly rendering and glViewport(0, 0, m_nDisplayWidth, m_nDisplayHeight) is working fine.
But when I want to load video twice the size of 2768 x 1557 I mean 5536 x 3114 the video shinked/congested (not truncated) across X axis. The Render(...) is drawing the full contents of video, but not using the full canvas. I can't figure it out what's wrong here. Why the video is congested across X axis?
It is to be noted that, the video is rendered more congested when I increased the width & height more than 2 times. Its okay till 2768 x 1557
You may be exceeding limits of your OpenGL implementation. Particularly, the maximum texture size and maximum viewport dimensions could come into play.
To query the maximum texture size, use:
GLint maxTexSize = 0;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
and for the maximum viewport dimensions:
GLint viewportDims[2] = {0};
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewportDims);
Typical values for these limits are as low as 2K for current low end devices, and possibly even lower for older devices. 4K and 8K are very common for current mainstream devices. Recent high end mobile GPUs support sizes up to 16K.
So before you try sizes over 4K, you should definitely check these limits. It's very likely that your 5536x3114 size will be beyond the limit of some fairly recent devices.

android 3d vertical carousel view

I am using http://www.codeproject.com/Articles/146145/Android-3D-Carousel code to create a vertical carousel view.i can see the vertical carousel using below changes in the code but center item is not properly placed in the screen and if the list items size increased, diameter moves upwards.
private void setUpChild(CarouselImageView child, int index, float angleOffset) {
// Ignore any layout parameters for child, use wrap content
addViewInLayout(child, -1 /*index*/, generateDefaultLayoutParams());
child.setSelected(index == mSelectedPosition);
int h;
int w;
if (mInLayout)
{
h = (getMeasuredHeight() - getPaddingBottom()-getPaddingTop())/3;
w = getMeasuredWidth() - getPaddingLeft() - getPaddingRight()/3;
}
else
{
h = (getMeasuredHeight() - getPaddingBottom()-getPaddingTop())/3;
w = getMeasuredWidth() - getPaddingLeft() - getPaddingRight()/3;
}
child.setCurrentAngle(angleOffset);
// modify the diameter.
Calculate3DPosition(child, w*(getAdapter().getCount()/4), angleOffset);
// Measure child
child.measure(w, h);
int childLeft;
// Position vertically based on gravity setting
int childTop = calculateTop(child, true);
childLeft = 0;
child.layout(childLeft, childTop, w, h);
}
change in calculate3position function as below
float x = (float) (-diameter/2 * Math.cos(angleOffset) * 0.00001);
float z = diameter/2 * (1.0f - (float)Math.cos(angleOffset));
float y = (float) (diameter/2 * Math.sin(angleOffset)) + diameter/2 - child.getWidth();
child.setX(x);
child.setZ(z);
child.setY(y);
I think that this calculation:
float x = (float) (-diameter/2 * Math.cos(angleOffset) * 0.00001);
float z = diameter/2 * (1.0f - (float)Math.cos(angleOffset));
float y = (float) (diameter/2 * Math.sin(angleOffset)) + diameter/2 - child.getWidth();
should be this:
float x = 0.0f
float z = diameter/2.0f * (1.0f - (float)Math.cos(angleOffset));
float y = (diameter/2.0f * Math.sin(angleOffset)) + diameter/2.0f - child.getHeight()/2.0f;
Your x position should always be zero, and your y position should be based on the sin, and should be offset by 1/2 of the height of the child instead of 1/2 of the width.
Hello try this code and replace with this code in your Calculate3DPosition method
angleOffset = angleOffset * (float) (Math.PI / 180.0f);
float y = (float) (((diameter * 60) / 100) * Math.sin(angleOffset)) + ((diameter * 50) / 100);
float z = diameter / 2 * (1.0f - (float) Math.cos(angleOffset));
float x = (float) (((diameter * 5) / 100) * Math.cos(angleOffset) * 0.3);
child.setItemX(x);
child.setItemZ((z * 30) / 100);
child.setItemY(-(y));
its solve my problem please try this one

Categories

Resources