Merge "Added benchmark mode. Added some image processing operations."
This commit is contained in:
committed by
Android (Google) Code Review
commit
d52498a64f
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
33
libs/rs/java/ImageProcessing/res/values/strings.xml
Normal file
33
libs/rs/java/ImageProcessing/res/values/strings.xml
Normal file
@@ -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>
|
||||
@@ -31,13 +31,35 @@ import android.view.SurfaceView;
|
||||
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 @@ public class ImageProcessingActivity extends Activity implements SurfaceHolder.C
|
||||
private Allocation mInPixelsAllocation;
|
||||
@SuppressWarnings({"FieldCanBeLocal"})
|
||||
private Allocation mOutPixelsAllocation;
|
||||
@SuppressWarnings({"FieldCanBeLocal"})
|
||||
private Allocation mScratchPixelsAllocation;
|
||||
|
||||
private SurfaceView mSurfaceView;
|
||||
private ImageView mDisplayView;
|
||||
@@ -66,34 +90,217 @@ public class ImageProcessingActivity extends Activity implements SurfaceHolder.C
|
||||
}
|
||||
|
||||
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 @@ public class ImageProcessingActivity extends Activity implements SurfaceHolder.C
|
||||
|
||||
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 @@ public class ImageProcessingActivity extends Activity implements SurfaceHolder.C
|
||||
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 @@ public class ImageProcessingActivity extends Activity implements SurfaceHolder.C
|
||||
|
||||
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 @@ public class ImageProcessingActivity extends Activity implements SurfaceHolder.C
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,23 @@ public class ScriptC_Threshold
|
||||
{
|
||||
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 @@ public class ScriptC_Threshold
|
||||
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 @@ public class ScriptC_Threshold
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user