Merge "Added benchmark mode. Added some image processing operations."
diff --git a/libs/rs/java/ImageProcessing/AndroidManifest.xml b/libs/rs/java/ImageProcessing/AndroidManifest.xml
index b48d208..d6a2db4 100644
--- a/libs/rs/java/ImageProcessing/AndroidManifest.xml
+++ b/libs/rs/java/ImageProcessing/AndroidManifest.xml
@@ -6,7 +6,8 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:label="Image Processing">
- <activity android:name="ImageProcessingActivity">
+ <activity android:name="ImageProcessingActivity"
+ android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rs/java/ImageProcessing/res/layout/main.xml b/libs/rs/java/ImageProcessing/res/layout/main.xml
index 6770c18..c6ec729 100644
--- a/libs/rs/java/ImageProcessing/res/layout/main.xml
+++ b/libs/rs/java/ImageProcessing/res/layout/main.xml
@@ -25,9 +25,147 @@
android:id="@+id/display"
android:layout_width="320dip"
android:layout_height="266dip" />
-
+
+ <Button
+ android:layout_marginBottom="170dip"
+ android:layout_width="wrap_content"
+ android:layout_height="40dip"
+ android:text="@string/benchmark"
+ android:onClick="benchmark"
+ android:layout_gravity="bottom"/>
+
+ <TextView
+ android:id="@+id/benchmarkText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:layout_marginLeft="100dip"
+ android:layout_marginBottom="175dip"
+ android:layout_gravity="bottom"
+ android:text="@string/saturation"/>
+
+ <SeekBar
+ android:id="@+id/inSaturation"
+ android:layout_marginBottom="140dip"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom" />
+
+ <TextView
+ android:id="@+id/inSaturationText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:layout_marginLeft="50dip"
+ android:layout_marginBottom="142dip"
+ android:textColor="#000"
+ android:layout_gravity="bottom"
+ android:text="@string/saturation"/>
+
<SeekBar
- android:id="@+id/threshold"
+ android:id="@+id/inGamma"
+ android:layout_marginBottom="110dip"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom" />
+
+ <TextView
+ android:id="@+id/inGammaText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:layout_marginLeft="50dip"
+ android:layout_marginBottom="112dip"
+ android:textColor="#000"
+ android:layout_gravity="bottom"
+ android:text="@string/gamma"/>
+
+ <SeekBar
+ android:id="@+id/outWhite"
+ android:layout_marginBottom="80dip"
+ android:layout_marginLeft="170dip"
+ android:layout_marginRight="10dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom" />
+
+ <TextView
+ android:id="@+id/outWhiteText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:layout_marginLeft="220dip"
+ android:layout_marginBottom="82dip"
+ android:textColor="#000"
+ android:layout_gravity="bottom"
+ android:text="@string/out_white"/>
+
+ <SeekBar
+ android:id="@+id/inWhite"
+ android:layout_marginBottom="80dip"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="170dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom" />
+
+ <TextView
+ android:id="@+id/inWhiteText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:layout_marginLeft="50dip"
+ android:layout_marginBottom="82dip"
+ android:textColor="#000"
+ android:layout_gravity="bottom"
+ android:text="@string/in_white"/>
+
+ <SeekBar
+ android:id="@+id/outBlack"
+ android:layout_marginBottom="50dip"
+ android:layout_marginLeft="170dip"
+ android:layout_marginRight="10dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom" />
+
+ <TextView
+ android:id="@+id/outBlackText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:layout_marginLeft="220dip"
+ android:layout_marginBottom="52dip"
+ android:textColor="#000"
+ android:layout_gravity="bottom"
+ android:text="@string/out_black"/>
+
+ <SeekBar
+ android:id="@+id/inBlack"
+ android:layout_marginBottom="50dip"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="170dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom" />
+
+ <TextView
+ android:id="@+id/inBlackText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:layout_marginLeft="50dip"
+ android:layout_marginBottom="52dip"
+ android:textColor="#000"
+ android:layout_gravity="bottom"
+ android:text="@string/in_black"/>
+
+ <SeekBar
+ android:id="@+id/radius"
android:layout_marginBottom="10dip"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
@@ -35,4 +173,15 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom" />
+ <TextView
+ android:id="@+id/blurText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:layout_marginLeft="50dip"
+ android:layout_marginBottom="12dip"
+ android:textColor="#000"
+ android:layout_gravity="bottom"
+ android:text="@string/blur_description"/>
+
</merge>
\ No newline at end of file
diff --git a/libs/rs/java/ImageProcessing/res/raw/threshold.rs b/libs/rs/java/ImageProcessing/res/raw/threshold.rs
index 8dc614e..0317088 100644
--- a/libs/rs/java/ImageProcessing/res/raw/threshold.rs
+++ b/libs/rs/java/ImageProcessing/res/raw/threshold.rs
@@ -4,46 +4,331 @@
#include "../../../../scriptc/rs_math.rsh"
#include "../../../../scriptc/rs_graphics.rsh"
+#define MAX_RADIUS 25
+
int height;
int width;
-float threshold;
+int radius;
typedef struct c4u_s {
- char r, g, b, a;
+ uint8_t r, g, b, a;
} c4u_t;
-//rs_color4u * InPixel;
-//rs_color4u * OutPixel;
c4u_t * InPixel;
c4u_t * OutPixel;
+c4u_t * ScratchPixel;
-#pragma rs export_var(height, width, threshold, InPixel, OutPixel)
+float inBlack;
+float outBlack;
+float inWhite;
+float outWhite;
+float gamma;
+
+float saturation;
+
+float inWMinInB;
+float outWMinOutB;
+
+#pragma rs export_var(height, width, radius, InPixel, OutPixel, ScratchPixel, inBlack, outBlack, inWhite, outWhite, gamma, saturation)
+#pragma rs export_func(filter, processNoBlur, computeColorMatrix, computeGaussianWeights);
+
+// Store our coefficients here
+float gaussian[MAX_RADIUS * 2 + 1];
+float colorMat[4][4];
+
+void computeColorMatrix() {
+ // Saturation
+ // Linear weights
+ //float rWeight = 0.3086f;
+ //float gWeight = 0.6094f;
+ //float bWeight = 0.0820f;
+
+ // Gamma 2.2 weights (we haven't converted our image to linear space yet for perf reasons)
+ float rWeight = 0.299f;
+ float gWeight = 0.587f;
+ float bWeight = 0.114f;
+
+ float oneMinusS = 1.0f - saturation;
+
+ matrixLoadIdentity(colorMat);
+
+ colorMat[0][0] = oneMinusS * rWeight + saturation;
+ colorMat[0][1] = oneMinusS * rWeight;
+ colorMat[0][2] = oneMinusS * rWeight;
+ colorMat[1][0] = oneMinusS * gWeight;
+ colorMat[1][1] = oneMinusS * gWeight + saturation;
+ colorMat[1][2] = oneMinusS * gWeight;
+ colorMat[2][0] = oneMinusS * bWeight;
+ colorMat[2][1] = oneMinusS * bWeight;
+ colorMat[2][2] = oneMinusS * bWeight + saturation;
+
+ inWMinInB = inWhite - inBlack;
+ outWMinOutB = outWhite - outBlack;
+}
+
+void computeGaussianWeights() {
+ // Compute gaussian weights for the blur
+ // e is the euler's number
+ float e = 2.718281828459045f;
+ float pi = 3.1415926535897932f;
+ // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
+ // x is of the form [-radius .. 0 .. radius]
+ // and sigma varies with radius.
+ // Based on some experimental radius values and sigma's
+ // we approximately fit sigma = f(radius) as
+ // sigma = radius * 0.4 + 0.6
+ // The larger the radius gets, the more our gaussian blur
+ // will resemble a box blur since with large sigma
+ // the gaussian curve begins to lose its shape
+ float sigma = 0.4f * (float)radius + 0.6f;
+
+ // Now compute the coefficints
+ // We will store some redundant values to save some math during
+ // the blur calculations
+ // precompute some values
+ float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
+ float coeff2 = - 1.0f / (2.0f * sigma * sigma);
+
+ float normalizeFactor = 0.0f;
+ float floatR = 0.0f;
+ int r;
+ for(r = -radius; r <= radius; r ++) {
+ floatR = (float)r;
+ gaussian[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
+ normalizeFactor += gaussian[r + radius];
+ }
+
+ //Now we need to normalize the weights because all our coefficients need to add up to one
+ normalizeFactor = 1.0f / normalizeFactor;
+ for(r = -radius; r <= radius; r ++) {
+ floatR = (float)r;
+ gaussian[r + radius] *= normalizeFactor;
+ }
+}
+
+// This needs to be inline
+void levelsSaturation(float4 *currentPixel) {
+ // Color matrix multiply
+ float tempX = colorMat[0][0] * currentPixel->x + colorMat[1][0] * currentPixel->y + colorMat[2][0] * currentPixel->z;
+ float tempY = colorMat[0][1] * currentPixel->x + colorMat[1][1] * currentPixel->y + colorMat[2][1] * currentPixel->z;
+ float tempZ = colorMat[0][2] * currentPixel->x + colorMat[1][2] * currentPixel->y + colorMat[2][2] * currentPixel->z;
+
+ currentPixel->x = tempX;
+ currentPixel->y = tempY;
+ currentPixel->z = tempZ;
+
+ // Clamp to 0..255
+ // Inline the code here to avoid funciton calls
+ currentPixel->x = currentPixel->x > 255.0f ? 255.0f : currentPixel->x;
+ currentPixel->y = currentPixel->y > 255.0f ? 255.0f : currentPixel->y;
+ currentPixel->z = currentPixel->z > 255.0f ? 255.0f : currentPixel->z;
+
+ currentPixel->x = currentPixel->x <= 0.0f ? 0.1f : currentPixel->x;
+ currentPixel->y = currentPixel->y <= 0.0f ? 0.1f : currentPixel->y;
+ currentPixel->z = currentPixel->z <= 0.0f ? 0.1f : currentPixel->z;
+
+ currentPixel->x = pow( (currentPixel->x - inBlack) / (inWMinInB), gamma) * (outWMinOutB) + outBlack;
+ currentPixel->y = pow( (currentPixel->y - inBlack) / (inWMinInB), gamma) * (outWMinOutB) + outBlack;
+ currentPixel->z = pow( (currentPixel->z - inBlack) / (inWMinInB), gamma) * (outWMinOutB) + outBlack;
+
+ currentPixel->x = currentPixel->x > 255.0f ? 255.0f : currentPixel->x;
+ currentPixel->y = currentPixel->y > 255.0f ? 255.0f : currentPixel->y;
+ currentPixel->z = currentPixel->z > 255.0f ? 255.0f : currentPixel->z;
+
+ currentPixel->x = currentPixel->x <= 0.0f ? 0.1f : currentPixel->x;
+ currentPixel->y = currentPixel->y <= 0.0f ? 0.1f : currentPixel->y;
+ currentPixel->z = currentPixel->z <= 0.0f ? 0.1f : currentPixel->z;
+}
+
+void processNoBlur() {
+ int w, h, r;
+ int count = 0;
+
+ float inWMinInB = inWhite - inBlack;
+ float outWMinOutB = outWhite - outBlack;
+ float4 currentPixel = 0;
+
+ for(h = 0; h < height; h ++) {
+ for(w = 0; w < width; w ++) {
+ c4u_t *input = InPixel + h*width + w;
+
+ currentPixel.x = (float)(input->r);
+ currentPixel.y = (float)(input->g);
+ currentPixel.z = (float)(input->b);
+
+ levelsSaturation(¤tPixel);
+
+ c4u_t *output = OutPixel + h*width + w;
+ output->r = (uint8_t)currentPixel.x;
+ output->g = (uint8_t)currentPixel.y;
+ output->b = (uint8_t)currentPixel.z;
+ output->a = input->a;
+ }
+ }
+ sendToClient(&count, 1, 4, 0);
+}
+
+void horizontalBlur() {
+ float4 blurredPixel = 0;
+ float4 currentPixel = 0;
+ // Horizontal blur
+ int w, h, r;
+ for(h = 0; h < height; h ++) {
+ for(w = 0; w < width; w ++) {
+
+ blurredPixel = 0;
+
+ for(r = -radius; r <= radius; r ++) {
+ // Stepping left and right away from the pixel
+ int validW = w + r;
+ // Clamp to zero and width max() isn't exposed for ints yet
+ if(validW < 0) {
+ validW = 0;
+ }
+ if(validW > width - 1) {
+ validW = width - 1;
+ }
+
+ c4u_t *input = InPixel + h*width + validW;
+
+ float weight = gaussian[r + radius];
+ currentPixel.x = (float)(input->r);
+ currentPixel.y = (float)(input->g);
+ currentPixel.z = (float)(input->b);
+ //currentPixel.w = (float)(input->a);
+
+ blurredPixel += currentPixel*weight;
+ }
+
+ c4u_t *output = ScratchPixel + h*width + w;
+ output->r = (uint8_t)blurredPixel.x;
+ output->g = (uint8_t)blurredPixel.y;
+ output->b = (uint8_t)blurredPixel.z;
+ //output->a = (uint8_t)blurredPixel.w;
+ }
+ }
+}
+
+void horizontalBlurLevels() {
+ float4 blurredPixel = 0;
+ float4 currentPixel = 0;
+ // Horizontal blur
+ int w, h, r;
+ for(h = 0; h < height; h ++) {
+ for(w = 0; w < width; w ++) {
+
+ blurredPixel = 0;
+
+ for(r = -radius; r <= radius; r ++) {
+ // Stepping left and right away from the pixel
+ int validW = w + r;
+ // Clamp to zero and width max() isn't exposed for ints yet
+ if(validW < 0) {
+ validW = 0;
+ }
+ if(validW > width - 1) {
+ validW = width - 1;
+ }
+
+ c4u_t *input = InPixel + h*width + validW;
+
+ float weight = gaussian[r + radius];
+ currentPixel.x = (float)(input->r);
+ currentPixel.y = (float)(input->g);
+ currentPixel.z = (float)(input->b);
+ //currentPixel.w = (float)(input->a);
+
+ blurredPixel += currentPixel*weight;
+ }
+
+ levelsSaturation(&blurredPixel);
+
+ c4u_t *output = ScratchPixel + h*width + w;
+ output->r = (uint8_t)blurredPixel.x;
+ output->g = (uint8_t)blurredPixel.y;
+ output->b = (uint8_t)blurredPixel.z;
+ //output->a = (uint8_t)blurredPixel.w;
+ }
+ }
+}
+
+void verticalBlur() {
+ float4 blurredPixel = 0;
+ float4 currentPixel = 0;
+ // Vertical blur
+ int w, h, r;
+ for(h = 0; h < height; h ++) {
+ for(w = 0; w < width; w ++) {
+
+ blurredPixel = 0;
+ for(r = -radius; r <= radius; r ++) {
+ int validH = h + r;
+ // Clamp to zero and width
+ if(validH < 0) {
+ validH = 0;
+ }
+ if(validH > height - 1) {
+ validH = height - 1;
+ }
+
+ c4u_t *input = ScratchPixel + validH*width + w;
+
+ float weight = gaussian[r + radius];
+
+ currentPixel.x = (float)(input->r);
+ currentPixel.y = (float)(input->g);
+ currentPixel.z = (float)(input->b);
+ //currentPixel.w = (float)(input->a);
+
+ blurredPixel += currentPixel*weight;
+ }
+
+ c4u_t *output = OutPixel + h*width + w;
+
+ output->r = (uint8_t)blurredPixel.x;
+ output->g = (uint8_t)blurredPixel.y;
+ output->b = (uint8_t)blurredPixel.z;
+ //output->a = (uint8_t)blurredPixel.w;
+ }
+ }
+}
void filter() {
debugP(0, (void *)height);
debugP(0, (void *)width);
- debugP(0, (void *)((int)threshold));
- debugP(0, (void *)InPixel);
- debugP(0, (void *)OutPixel);
+ debugP(0, (void *)radius);
- rs_color4u *in = (rs_color4u *)InPixel;
- rs_color4u *out = (rs_color4u *)OutPixel;
- //const rs_color4u mask = {0,0,0,0xff};
+ debugPf(10, inBlack);
+ debugPf(11, outBlack);
+ debugPf(12, inWhite);
+ debugPf(13, outWhite);
+ debugPf(14, gamma);
+ debugPf(15, saturation);
- int count = width * height;
- int tf = threshold * 255 * 255;
- int masks[2] = {0xffffffff, 0xff000000};
+ computeColorMatrix();
- while (count--) {
- int luminance = 54 * in->x +
- 182 * in->y +
- 18 * in->z;
- int idx = ((uint32_t)(luminance - tf)) >> 31;
- *((int *)out) = *((int *)in) & masks[idx];
- in++;
- out++;
+ if(radius == 0) {
+ processNoBlur();
+ return;
}
+ computeGaussianWeights();
+
+ horizontalBlurLevels();
+ verticalBlur();
+
+ int count = 0;
+ sendToClient(&count, 1, 4, 0);
+}
+
+void filterBenchmark() {
+
+ computeGaussianWeights();
+
+ horizontalBlur();
+ verticalBlur();
+
+ int count = 0;
sendToClient(&count, 1, 4, 0);
}
diff --git a/libs/rs/java/ImageProcessing/res/raw/threshold_bc.bc b/libs/rs/java/ImageProcessing/res/raw/threshold_bc.bc
index 55d514c..58a93e6 100644
--- a/libs/rs/java/ImageProcessing/res/raw/threshold_bc.bc
+++ b/libs/rs/java/ImageProcessing/res/raw/threshold_bc.bc
Binary files differ
diff --git a/libs/rs/java/ImageProcessing/res/values/strings.xml b/libs/rs/java/ImageProcessing/res/values/strings.xml
new file mode 100644
index 0000000..cc5cc4d
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/res/values/strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <skip />
+ <!--slider label -->
+ <string name="blur_description">Blur Radius</string>
+ <string name="in_white">In White</string>
+ <string name="out_white">Out White</string>
+ <string name="in_black">In Black</string>
+ <string name="out_black">Out Black</string>
+ <string name="gamma">Gamma</string>
+ <string name="saturation">Saturation</string>
+ <string name="benchmark">Benchmark</string>
+
+</resources>
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index aec5e23..5e36a78 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -31,13 +31,35 @@
import android.view.SurfaceHolder;
import android.widget.ImageView;
import android.widget.SeekBar;
+import android.widget.TextView;
+import android.view.View;
import java.lang.Math;
-public class ImageProcessingActivity extends Activity implements SurfaceHolder.Callback {
+public class ImageProcessingActivity extends Activity
+ implements SurfaceHolder.Callback,
+ SeekBar.OnSeekBarChangeListener {
private Bitmap mBitmapIn;
private Bitmap mBitmapOut;
+ private Bitmap mBitmapScratch;
private ScriptC_Threshold mScript;
- private float mThreshold = 0.5f;
+ private int mRadius = 0;
+ private SeekBar mRadiusSeekBar;
+
+ private float mInBlack = 0.0f;
+ private SeekBar mInBlackSeekBar;
+ private float mOutBlack = 0.0f;
+ private SeekBar mOutBlackSeekBar;
+ private float mInWhite = 255.0f;
+ private SeekBar mInWhiteSeekBar;
+ private float mOutWhite = 255.0f;
+ private SeekBar mOutWhiteSeekBar;
+ private float mGamma = 1.0f;
+ private SeekBar mGammaSeekBar;
+
+ private float mSaturation = 1.0f;
+ private SeekBar mSaturationSeekBar;
+
+ private TextView mBenchmarkResult;
@SuppressWarnings({"FieldCanBeLocal"})
private RenderScript mRS;
@@ -47,6 +69,8 @@
private Allocation mInPixelsAllocation;
@SuppressWarnings({"FieldCanBeLocal"})
private Allocation mOutPixelsAllocation;
+ @SuppressWarnings({"FieldCanBeLocal"})
+ private Allocation mScratchPixelsAllocation;
private SurfaceView mSurfaceView;
private ImageView mDisplayView;
@@ -66,34 +90,217 @@
}
int in[];
+ int interm[];
int out[];
- private void javaFilter() {
- final int w = mBitmapIn.getWidth();
- final int h = mBitmapIn.getHeight();
- final int count = w * h;
+ int MAX_RADIUS = 25;
+ // Store our coefficients here
+ float gaussian[];
+
+ private long javaFilter() {
+ final int width = mBitmapIn.getWidth();
+ final int height = mBitmapIn.getHeight();
+ final int count = width * height;
if (in == null) {
in = new int[count];
+ interm = new int[count];
out = new int[count];
- mBitmapIn.getPixels(in, 0, w, 0, 0, w, h);
+ gaussian = new float[MAX_RADIUS * 2 + 1];
+ mBitmapIn.getPixels(in, 0, width, 0, 0, width, height);
}
- int threshold = (int)(mThreshold * 255.f) * 255;
- //long t = java.lang.System.currentTimeMillis();
+ long t = java.lang.System.currentTimeMillis();
- for (int i = 0; i < count; i++) {
- final int luminance = 54 * ((in[i] >> 0) & 0xff) +
- 182* ((in[i] >> 8) & 0xff) +
- 18 * ((in[i] >> 16) & 0xff);
- if (luminance > threshold) {
- out[i] = in[i];
- } else {
- out[i] = in[i] & 0xff000000;
+ int w, h, r;
+
+ float fRadius = (float)mRadius;
+ int radius = (int)mRadius;
+
+ // Compute gaussian weights for the blur
+ // e is the euler's number
+ float e = 2.718281828459045f;
+ float pi = 3.1415926535897932f;
+ // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
+ // x is of the form [-radius .. 0 .. radius]
+ // and sigma varies with radius.
+ // Based on some experimental radius values and sigma's
+ // we approximately fit sigma = f(radius) as
+ // sigma = radius * 0.4 + 0.6
+ // The larger the radius gets, the more our gaussian blur
+ // will resemble a box blur since with large sigma
+ // the gaussian curve begins to lose its shape
+ float sigma = 0.4f * fRadius + 0.6f;
+ // Now compute the coefficints
+ // We will store some redundant values to save some math during
+ // the blur calculations
+ // precompute some values
+ float coeff1 = 1.0f / (float)(Math.sqrt( 2.0f * pi ) * sigma);
+ float coeff2 = - 1.0f / (2.0f * sigma * sigma);
+ float normalizeFactor = 0.0f;
+ float floatR = 0.0f;
+ for(r = -radius; r <= radius; r ++) {
+ floatR = (float)r;
+ gaussian[r + radius] = coeff1 * (float)Math.pow(e, floatR * floatR * coeff2);
+ normalizeFactor += gaussian[r + radius];
+ }
+
+ //Now we need to normalize the weights because all our coefficients need to add up to one
+ normalizeFactor = 1.0f / normalizeFactor;
+ for(r = -radius; r <= radius; r ++) {
+ floatR = (float)r;
+ gaussian[r + radius] *= normalizeFactor;
+ }
+
+ float blurredPixelR = 0.0f;
+ float blurredPixelG = 0.0f;
+ float blurredPixelB = 0.0f;
+ float blurredPixelA = 0.0f;
+
+ for(h = 0; h < height; h ++) {
+ for(w = 0; w < width; w ++) {
+
+ blurredPixelR = 0.0f;
+ blurredPixelG = 0.0f;
+ blurredPixelB = 0.0f;
+ blurredPixelA = 0.0f;
+
+ for(r = -radius; r <= radius; r ++) {
+ // Stepping left and right away from the pixel
+ int validW = w + r;
+ // Clamp to zero and width max() isn't exposed for ints yet
+ if(validW < 0) {
+ validW = 0;
+ }
+ if(validW > width - 1) {
+ validW = width - 1;
+ }
+
+ int input = in[h*width + validW];
+
+ int R = ((input >> 24) & 0xff);
+ int G = ((input >> 16) & 0xff);
+ int B = ((input >> 8) & 0xff);
+ int A = (input & 0xff);
+
+ float weight = gaussian[r + radius];
+
+ blurredPixelR += (float)(R)*weight;
+ blurredPixelG += (float)(G)*weight;
+ blurredPixelB += (float)(B)*weight;
+ blurredPixelA += (float)(A)*weight;
+ }
+
+ int R = (int)blurredPixelR;
+ int G = (int)blurredPixelG;
+ int B = (int)blurredPixelB;
+ int A = (int)blurredPixelA;
+
+ interm[h*width + w] = (R << 24) | (G << 16) | (B << 8) | (A);
}
}
- //t = java.lang.System.currentTimeMillis() - t;
- //android.util.Log.v("Img", "frame time ms " + t);
- mBitmapOut.setPixels(out, 0, w, 0, 0, w, h);
+
+ for(h = 0; h < height; h ++) {
+ for(w = 0; w < width; w ++) {
+
+ blurredPixelR = 0.0f;
+ blurredPixelG = 0.0f;
+ blurredPixelB = 0.0f;
+ blurredPixelA = 0.0f;
+ for(r = -radius; r <= radius; r ++) {
+ int validH = h + r;
+ // Clamp to zero and width
+ if(validH < 0) {
+ validH = 0;
+ }
+ if(validH > height - 1) {
+ validH = height - 1;
+ }
+
+ int input = interm[validH*width + w];
+
+ int R = ((input >> 24) & 0xff);
+ int G = ((input >> 16) & 0xff);
+ int B = ((input >> 8) & 0xff);
+ int A = (input & 0xff);
+
+ float weight = gaussian[r + radius];
+
+ blurredPixelR += (float)(R)*weight;
+ blurredPixelG += (float)(G)*weight;
+ blurredPixelB += (float)(B)*weight;
+ blurredPixelA += (float)(A)*weight;
+ }
+
+ int R = (int)blurredPixelR;
+ int G = (int)blurredPixelG;
+ int B = (int)blurredPixelB;
+ int A = (int)blurredPixelA;
+
+ out[h*width + w] = (R << 24) | (G << 16) | (B << 8) | (A);
+ }
+ }
+
+ t = java.lang.System.currentTimeMillis() - t;
+ android.util.Log.v("Img", "Java frame time ms " + t);
+ mBitmapOut.setPixels(out, 0, width, 0, 0, width, height);
+ return t;
+ }
+
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (fromUser) {
+
+ if(seekBar == mRadiusSeekBar) {
+ float fRadius = progress / 100.0f;
+ fRadius *= (float)(MAX_RADIUS);
+ mRadius = (int)fRadius;
+
+ mScript.set_radius(mRadius);
+ }
+ else if(seekBar == mInBlackSeekBar) {
+ mInBlack = (float)progress;
+ mScript.set_inBlack(mInBlack);
+ }
+ else if(seekBar == mOutBlackSeekBar) {
+ mOutBlack = (float)progress;
+ mScript.set_outBlack(mOutBlack);
+ }
+ else if(seekBar == mInWhiteSeekBar) {
+ mInWhite = (float)progress + 127.0f;
+ mScript.set_inWhite(mInWhite);
+ }
+ else if(seekBar == mOutWhiteSeekBar) {
+ mOutWhite = (float)progress + 127.0f;
+ mScript.set_outWhite(mOutWhite);
+ }
+ else if(seekBar == mGammaSeekBar) {
+ mGamma = (float)progress/100.0f;
+ mGamma = Math.max(mGamma, 0.1f);
+ mGamma = 1.0f / mGamma;
+ mScript.set_gamma(mGamma);
+ }
+ else if(seekBar == mSaturationSeekBar) {
+ mSaturation = (float)progress / 50.0f;
+ mScript.set_saturation(mSaturation);
+ }
+
+ long t = java.lang.System.currentTimeMillis();
+ if (true) {
+ mScript.invokable_Filter();
+ } else {
+ javaFilter();
+ }
+
+ t = java.lang.System.currentTimeMillis() - t;
+ android.util.Log.v("Img", "Renderscript frame time core ms " + t);
+
+ mDisplayView.invalidate();
+ }
+ }
+
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ public void onStopTrackingTouch(SeekBar seekBar) {
}
@Override
@@ -103,6 +310,7 @@
mBitmapIn = loadBitmap(R.drawable.data);
mBitmapOut = loadBitmap(R.drawable.data);
+ mBitmapScratch = loadBitmap(R.drawable.data);
mSurfaceView = (SurfaceView) findViewById(R.id.surface);
mSurfaceView.getHolder().addCallback(this);
@@ -110,31 +318,38 @@
mDisplayView = (ImageView) findViewById(R.id.display);
mDisplayView.setImageBitmap(mBitmapOut);
- ((SeekBar) findViewById(R.id.threshold)).setOnSeekBarChangeListener(
- new SeekBar.OnSeekBarChangeListener() {
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (fromUser) {
- mThreshold = progress / 100.0f;
- mScript.set_threshold(mThreshold);
+ mRadiusSeekBar = (SeekBar) findViewById(R.id.radius);
+ mRadiusSeekBar.setOnSeekBarChangeListener(this);
- long t = java.lang.System.currentTimeMillis();
- if (true) {
- mScript.invokable_Filter();
- } else {
- javaFilter();
- mDisplayView.invalidate();
- }
- t = java.lang.System.currentTimeMillis() - t;
- android.util.Log.v("Img", "frame time core ms " + t);
- }
- }
+ mInBlackSeekBar = (SeekBar)findViewById(R.id.inBlack);
+ mInBlackSeekBar.setOnSeekBarChangeListener(this);
+ mInBlackSeekBar.setMax(128);
+ mInBlackSeekBar.setProgress(0);
+ mOutBlackSeekBar = (SeekBar)findViewById(R.id.outBlack);
+ mOutBlackSeekBar.setOnSeekBarChangeListener(this);
+ mOutBlackSeekBar.setMax(128);
+ mOutBlackSeekBar.setProgress(0);
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
+ mInWhiteSeekBar = (SeekBar)findViewById(R.id.inWhite);
+ mInWhiteSeekBar.setOnSeekBarChangeListener(this);
+ mInWhiteSeekBar.setMax(128);
+ mInWhiteSeekBar.setProgress(128);
+ mOutWhiteSeekBar = (SeekBar)findViewById(R.id.outWhite);
+ mOutWhiteSeekBar.setOnSeekBarChangeListener(this);
+ mOutWhiteSeekBar.setMax(128);
+ mOutWhiteSeekBar.setProgress(128);
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
+ mGammaSeekBar = (SeekBar)findViewById(R.id.inGamma);
+ mGammaSeekBar.setOnSeekBarChangeListener(this);
+ mGammaSeekBar.setMax(150);
+ mGammaSeekBar.setProgress(100);
+
+ mSaturationSeekBar = (SeekBar)findViewById(R.id.inSaturation);
+ mSaturationSeekBar.setOnSeekBarChangeListener(this);
+ mSaturationSeekBar.setProgress(50);
+
+ mBenchmarkResult = (TextView) findViewById(R.id.benchmarkText);
+ mBenchmarkResult.setText("Benchmark no yet run");
}
public void surfaceCreated(SurfaceHolder holder) {
@@ -154,13 +369,23 @@
mInPixelsAllocation = Allocation.createBitmapRef(mRS, mBitmapIn);
mOutPixelsAllocation = Allocation.createBitmapRef(mRS, mBitmapOut);
+ mScratchPixelsAllocation = Allocation.createBitmapRef(mRS, mBitmapScratch);
mScript = new ScriptC_Threshold(mRS, getResources(), false);
mScript.set_width(mBitmapIn.getWidth());
mScript.set_height(mBitmapIn.getHeight());
- mScript.set_threshold(mThreshold);
+ mScript.set_radius(mRadius);
+
+ mScript.set_inBlack(mInBlack);
+ mScript.set_outBlack(mOutBlack);
+ mScript.set_inWhite(mInWhite);
+ mScript.set_outWhite(mOutWhite);
+ mScript.set_gamma(mGamma);
+ mScript.set_saturation(mSaturation);
+
mScript.bind_InPixel(mInPixelsAllocation);
mScript.bind_OutPixel(mOutPixelsAllocation);
+ mScript.bind_ScratchPixel(mScratchPixelsAllocation);
}
private Bitmap loadBitmap(int resource) {
@@ -176,4 +401,28 @@
source.recycle();
return b;
}
+
+ // button hook
+ public void benchmark(View v) {
+ android.util.Log.v("Img", "Benchmarking");
+ int oldRadius = mRadius;
+ mRadius = MAX_RADIUS;
+ mScript.set_radius(mRadius);
+
+ long t = java.lang.System.currentTimeMillis();
+
+ mScript.invokable_FilterBenchmark();
+
+ t = java.lang.System.currentTimeMillis() - t;
+ android.util.Log.v("Img", "Renderscript frame time core ms " + t);
+
+ long javaTime = javaFilter();
+
+ mBenchmarkResult.setText("RS: " + t + " ms Java: " + javaTime + " ms");
+
+ mRadius = oldRadius;
+ mScript.set_radius(mRadius);
+
+ mScript.invokable_Filter();
+ }
}
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Threshold.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Threshold.java
index dbaa18c..dfed9e5 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Threshold.java
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Threshold.java
@@ -10,12 +10,23 @@
{
private final static int mFieldIndex_height = 0;
private final static int mFieldIndex_width = 1;
- private final static int mFieldIndex_threshold = 2;
+ private final static int mFieldIndex_radius = 2;
private final static int mFieldIndex_InPixel = 3;
private final static int mFieldIndex_OutPixel = 4;
+ private final static int mFieldIndex_ScratchPixel = 5;
+
+ private final static int mFieldIndex_inBlack = 6;
+ private final static int mFieldIndex_outBlack = 7;
+ private final static int mFieldIndex_inWhite = 8;
+ private final static int mFieldIndex_outWhite = 9;
+ private final static int mFieldIndex_gamma = 10;
+
+ private final static int mFieldIndex_saturation = 11;
+ private final static int mFieldIndex_hue = 12;
private Allocation mField_InPixel;
private Allocation mField_OutPixel;
+ private Allocation mField_ScratchPixel;
public ScriptC_Threshold(RenderScript rs, Resources resources, boolean isRoot) {
super(rs, resources, R.raw.threshold_bc, isRoot);
@@ -43,6 +54,15 @@
bindAllocation(f, mFieldIndex_OutPixel);
mField_OutPixel = f;
}
+ public void bind_ScratchPixel(Allocation f) {
+ if (f != null) {
+ //if (f.getType().getElement() != Element.ATTRIB_COLOR_U8_4(mRS)) {
+ //throw new IllegalArgumentException("Element type mismatch.");
+ //}
+ }
+ bindAllocation(f, mFieldIndex_ScratchPixel);
+ mField_ScratchPixel = f;
+ }
public Allocation get_OutPixel() {
return mField_OutPixel;
}
@@ -55,13 +75,41 @@
setVar(mFieldIndex_width, v);
}
- public void set_threshold(float v) {
- setVar(mFieldIndex_threshold, v);
+ public void set_radius(int v) {
+ setVar(mFieldIndex_radius, v);
}
- private final static int mInvokableIndex_Filter = 0;
+ public void set_inBlack(float v) {
+ setVar(mFieldIndex_inBlack, v);
+ }
+ public void set_outBlack(float v) {
+ setVar(mFieldIndex_outBlack, v);
+ }
+ public void set_inWhite(float v) {
+ setVar(mFieldIndex_inWhite, v);
+ }
+ public void set_outWhite(float v) {
+ setVar(mFieldIndex_outWhite, v);
+ }
+ public void set_gamma(float v) {
+ setVar(mFieldIndex_gamma, v);
+ }
+
+ public void set_saturation(float v) {
+ setVar(mFieldIndex_saturation, v);
+ }
+ public void set_hue(float v) {
+ setVar(mFieldIndex_hue, v);
+ }
+
+ private final static int mInvokableIndex_Filter = 2;
public void invokable_Filter() {
invokeData(mInvokableIndex_Filter);
}
+
+ private final static int mInvokableIndex_FilterBenchmark = 3;
+ public void invokable_FilterBenchmark() {
+ invokeData(mInvokableIndex_FilterBenchmark);
+ }
}