add redeye and improve shadow removal
bug:7234321
Change-Id: I12c2eb28555d7594fddf86dfa224219b70137681
diff --git a/jni/Android.mk b/jni/Android.mk
index 213663c..92789c1 100644
--- a/jni/Android.mk
+++ b/jni/Android.mk
@@ -34,7 +34,9 @@
filters/hsv.c \
filters/vibrance.c \
filters/geometry.c \
- filters/vignette.c
+ filters/vignette.c \
+ filters/redEyeMath.c \
+ filters/redeye.c
LOCAL_CFLAGS += -ffast-math -O3 -funroll-loops
LOCAL_ARM_MODE := arm
diff --git a/jni/filters/filters.h b/jni/filters/filters.h
index 954d02d..d8728f0 100644
--- a/jni/filters/filters.h
+++ b/jni/filters/filters.h
@@ -47,5 +47,6 @@
extern void rgb2hsv( unsigned char *rgb,int rgbOff,unsigned short *hsv,int hsvOff);
extern void hsv2rgb(unsigned short *hsv,int hsvOff,unsigned char *rgb,int rgbOff);
-
+extern void filterRedEye(unsigned char *src, unsigned char *dest, int iw, int ih, short *rect);
+extern double fastevalPoly(double *poly,int n, double x);
#endif // FILTERS_H
diff --git a/jni/filters/hsv.c b/jni/filters/hsv.c
index af438f8..aabd053 100644
--- a/jni/filters/hsv.c
+++ b/jni/filters/hsv.c
@@ -17,128 +17,140 @@
#include <math.h>
#include "filters.h"
+double fastevalPoly(double *poly,int n, double x){
+
+ double f =x;
+ double sum = poly[0]+poly[1]*f;
+ int i;
+ for (i = 2; i < n; i++) {
+ f*=x;
+ sum += poly[i]*f;
+ }
+ return sum;
+}
+
void rgb2hsv( unsigned char *rgb,int rgbOff,unsigned short *hsv,int hsvOff)
{
- int iMin,iMax,chroma;
- int ABITS = 4;
- int HSCALE = 256;
+ int iMin,iMax,chroma;
+ int ABITS = 4;
+ int HSCALE = 256;
- int k1=255 << ABITS;
- int k2=HSCALE << ABITS;
+ int k1=255 << ABITS;
+ int k2=HSCALE << ABITS;
- int ri = rgb[rgbOff+0];
- int gi = rgb[rgbOff+1];
- int bi = rgb[rgbOff+2];
- short rv,rs,rh;
+ int ri = rgb[rgbOff+0];
+ int gi = rgb[rgbOff+1];
+ int bi = rgb[rgbOff+2];
+ short rv,rs,rh;
- if (ri > gi) {
- iMax = MAX (ri, bi);
- iMin = MIN (gi, bi);
- } else {
- iMax = MAX (gi, bi);
- iMin = MIN (ri, bi);
- }
-
- chroma = iMax - iMin;
- // set value
- rv = (short)( iMax << ABITS);
-
- // set saturation
- if (rv == 0)
- rs = 0;
- else
- rs = (short)((k1*chroma)/iMax);
-
- // set hue
- if (rs == 0)
- rh = 0;
- else {
- if ( ri == iMax ) {
- rh = (short)( (k2*(6*chroma+gi - bi))/(6*chroma));
- if (rh >= k2) rh -= k2;
- } else if (gi == iMax)
- rh = (short)( (k2*(2*chroma+bi - ri ))/(6*chroma));
- else // (bi == iMax )
- rh = (short)( (k2*(4*chroma+ri - gi ))/(6*chroma));
- }
- hsv[hsvOff+0] = rv;
- hsv[hsvOff+1] = rs;
- hsv[hsvOff+2] = rh;
+ if (ri > gi) {
+ iMax = MAX (ri, bi);
+ iMin = MIN (gi, bi);
+ } else {
+ iMax = MAX (gi, bi);
+ iMin = MIN (ri, bi);
}
- void hsv2rgb(unsigned short *hsv,int hsvOff, unsigned char *rgb,int rgbOff)
- {
- int ABITS = 4;
- int HSCALE = 256;
- int m;
- int H,X,ih,is,iv;
- int k1=255<<ABITS;
- int k2=HSCALE<<ABITS;
- int k3=1<<(ABITS-1);
- int rr=0;
- int rg=0;
- int rb=0;
- short cv = hsv[hsvOff+0];
- short cs = hsv[hsvOff+1];
- short ch = hsv[hsvOff+2];
+ chroma = iMax - iMin;
+ // set value
+ rv = (short)( iMax << ABITS);
- // set chroma and min component value m
- //chroma = ( cv * cs )/k1;
- //m = cv - chroma;
- m = ((int)cv*(k1 - (int)cs ))/k1;
+ // set saturation
+ if (rv == 0)
+ rs = 0;
+ else
+ rs = (short)((k1*chroma)/iMax);
- // chroma == 0 <-> cs == 0 --> m=cv
- if (cs == 0) {
- rb = ( rg = ( rr =( cv >> ABITS) ));
- } else {
- ih=(int)ch;
- is=(int)cs;
- iv=(int)cv;
+ // set hue
+ if (rs == 0)
+ rh = 0;
+ else {
+ if ( ri == iMax ) {
+ rh = (short)( (k2*(6*chroma+gi - bi))/(6*chroma));
+ if (rh >= k2) rh -= k2;
+ } else if (gi == iMax)
+ rh = (short)( (k2*(2*chroma+bi - ri ))/(6*chroma));
+ else // (bi == iMax )
+ rh = (short)( (k2*(4*chroma+ri - gi ))/(6*chroma));
+ }
+ hsv[hsvOff+0] = rv;
+ hsv[hsvOff+1] = rs;
+ hsv[hsvOff+2] = rh;
+}
- H = (6*ih)/k2;
- X = ((iv*is)/k2)*(k2- abs(6*ih- 2*(H>>1)*k2 - k2)) ;
+void hsv2rgb(unsigned short *hsv,int hsvOff, unsigned char *rgb,int rgbOff)
+{
+ int ABITS = 4;
+ int HSCALE = 256;
+ int m;
+ int H,X,ih,is,iv;
+ int k1=255<<ABITS;
+ int k2=HSCALE<<ABITS;
+ int k3=1<<(ABITS-1);
+ int rr=0;
+ int rg=0;
+ int rb=0;
+ short cv = hsv[hsvOff+0];
+ short cs = hsv[hsvOff+1];
+ short ch = hsv[hsvOff+2];
- // removing additional bits --> unit8
- X=( (X+iv*(k1 - is ))/k1 + k3 ) >> ABITS;
- m=m >> ABITS;
+ // set chroma and min component value m
+ //chroma = ( cv * cs )/k1;
+ //m = cv - chroma;
+ m = ((int)cv*(k1 - (int)cs ))/k1;
- // ( chroma + m ) --> cv ;
- cv=(short) (cv >> ABITS);
- switch (H) {
- case 0:
- rr = cv;
- rg = X;
- rb = m;
- break;
- case 1:
- rr = X;
- rg = cv;
- rb = m;
- break;
- case 2:
- rr = m;
- rg = cv;
- rb = X;
- break;
- case 3:
- rr = m;
- rg = X;
- rb = cv;
- break;
- case 4:
- rr = X;
- rg = m;
- rb = cv;
- break;
- case 5:
- rr = cv;
- rg = m ;
- rb = X;
- break;
- }
- }
- rgb[rgbOff+0] = rr;
- rgb[rgbOff+1] = rg;
- rgb[rgbOff+2] = rb;
- }
+ // chroma == 0 <-> cs == 0 --> m=cv
+ if (cs == 0) {
+ rb = ( rg = ( rr =( cv >> ABITS) ));
+ } else {
+ ih=(int)ch;
+ is=(int)cs;
+ iv=(int)cv;
+
+ H = (6*ih)/k2;
+ X = ((iv*is)/k2)*(k2- abs(6*ih- 2*(H>>1)*k2 - k2)) ;
+
+ // removing additional bits --> unit8
+ X=( (X+iv*(k1 - is ))/k1 + k3 ) >> ABITS;
+ m=m >> ABITS;
+
+ // ( chroma + m ) --> cv ;
+ cv=(short) (cv >> ABITS);
+ switch (H) {
+ case 0:
+ rr = cv;
+ rg = X;
+ rb = m;
+ break;
+ case 1:
+ rr = X;
+ rg = cv;
+ rb = m;
+ break;
+ case 2:
+ rr = m;
+ rg = cv;
+ rb = X;
+ break;
+ case 3:
+ rr = m;
+ rg = X;
+ rb = cv;
+ break;
+ case 4:
+ rr = X;
+ rg = m;
+ rb = cv;
+ break;
+ case 5:
+ rr = cv;
+ rg = m ;
+ rb = X;
+ break;
+ }
+ }
+ rgb[rgbOff+0] = rr;
+ rgb[rgbOff+1] = rg;
+ rgb[rgbOff+2] = rb;
+}
diff --git a/jni/filters/redEyeMath.c b/jni/filters/redEyeMath.c
new file mode 100644
index 0000000..26f3f76
--- /dev/null
+++ b/jni/filters/redEyeMath.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <math.h>
+#include "filters.h"
+
+int value(int r, int g, int b) {
+ return MAX(r, MAX(g, b));
+}
+
+int isRed(unsigned char *src, int p) {
+ int b = src[p + 2];
+ int g = src[p + 1];
+ int r = src[p];
+ int max = MAX(g, b);
+
+ return ((r * 100 / (max + 2) > 160) & (max < 80));
+}
+
+void findPossible(unsigned char *src, unsigned char *mask, int iw, int ih,
+ short *rect) {
+ int recX = rect[0], recY = rect[1], recW = rect[2], recH = rect[3];
+ int y, x;
+
+ for (y = 0; y < recH; y++) {
+ int sy = (recY + y) * iw;
+ for (x = 0; x < recW; x++) {
+ int p = (recX + x + sy) * 4;
+
+ int b = src[p + 2];
+ int g = src[p + 1];
+ int r = src[p];
+ mask[x + y * recW] = (
+ mask[x + y * recW] > 0 && (value(r, g, b) > 240) ? 1 : 0);
+
+ }
+
+ }
+}
+
+void findReds(unsigned char *src, unsigned char *mask, int iw, int ih,
+ short *rect) {
+ int recX = rect[0], recY = rect[1], recW = rect[2], recH = rect[3];
+ int y, x;
+
+ for (y = 0; y < recH; y++) {
+ int sy = (recY + y) * iw;
+ for (x = 0; x < recW; x++) {
+ int p = (recX + x + sy) * 4;
+
+ mask[x + y * recW] = ((isRed(src, p)) ? 1 : 0);
+
+ }
+
+ }
+}
+
+void dialateMaskIfRed(unsigned char *src, int iw, int ih, unsigned char *mask,
+ unsigned char *out, short *rect) {
+ int recX = rect[0], recY = rect[1], recW = rect[2], recH = rect[3];
+ int y, x;
+
+ for (y = 1; y < recH - 1; y++) {
+ int row = recW * y;
+ int sy = (recY + y) * iw;
+ for (x = 1; x < recW - 1; x++) {
+ int p = (recX + x + sy) * 4;
+
+ char b = (mask[row + x] | mask[row + x + 1] | mask[row + x - 1]
+ | mask[row + x - recW] | mask[row + x + recW]);
+ if (b != 0 && isRed(src, p))
+ out[row + x] = 1;
+ else
+ out[row + x] = mask[row + x];
+ }
+ }
+}
+
+void dialateMask(unsigned char *mask, unsigned char *out, int mw, int mh) {
+ int y, x;
+ for (y = 1; y < mh - 1; y++) {
+ int row = mw * y;
+ for (x = 1; x < mw - 1; x++) {
+ out[row + x] = (mask[row + x] | mask[row + x + 1]
+ | mask[row + x - 1] | mask[row + x - mw]
+ | mask[row + x + mw]);
+ }
+ }
+}
+
+void stuff(int r, int g, int b, unsigned char *img, int off) {
+ img[off + 2] = b;
+ img[off + 1] = g;
+ img[off] = r;
+}
+
+void filterRedEye(unsigned char *src, unsigned char *dest, int iw, int ih, short *rect) {
+ int recX = rect[0], recY = rect[1], recW = rect[2], recH = rect[3];
+ unsigned char *mask1 = (unsigned char *) malloc(recW * recH);
+ unsigned char *mask2 = (unsigned char *)malloc(recW*recH);
+ int QUE_LEN = 100;
+ int y, x, i;
+
+ rect[0] = MAX(rect[0],0);
+ rect[1] = MAX(rect[1],0);
+ rect[2] = MIN(rect[2]+rect[0],iw)-rect[0];
+ rect[3] = MIN(rect[3]+rect[1],ih)-rect[1];
+
+ findReds(src, mask2, iw, ih, rect);
+ dialateMask(mask2, mask1, recW, recH);
+ dialateMask(mask1, mask2, recW, recH);
+ dialateMask(mask2, mask1, recW, recH);
+ dialateMask(mask1, mask2, recW, recH);
+ findPossible(src, mask2, iw, ih, rect);
+ dialateMask(mask2, mask1, recW, recH);
+
+ for (i = 0; i < 12; i++) {
+ dialateMaskIfRed(src, iw, ih, mask1, mask2, rect);
+ dialateMaskIfRed(src, iw, ih, mask2, mask1, rect);
+ }
+ dialateMask(mask1, mask2, recW, recH);
+ dialateMask(mask2, mask1, recW, recH);
+
+ for (y = 3; y < recH-3; y++) {
+ int sy = (recY + y) * iw;
+ for (x = 3; x < recW-3; x++) {
+ int p = (recX + x + sy) * 4;
+
+ int b = src[p + 2];
+ int g = src[p + 1];
+ int r = src[p];
+
+ if (mask1[x + y * recW] != 0) {
+ int m = MAX(g,b);
+ float rr = (r - m) / (float) m;
+ if (rr > .7f && g < 60 && b < 60) {
+ dest[p + 2] = (0);
+ dest[p + 1] = (0);
+ dest[p] = (0);
+ } else {
+ if (mask2[x + y * recW] != 0) {
+ stuff(r / 2, g / 2, b / 2, dest, p);
+ } else
+ stuff((2 * r) / 3, (2 * g) / 3, (2 * b) / 3, dest, p);
+ }
+
+ } else
+ stuff(r, g, b, dest, p);
+
+ //dest[p + 2] = dest[p + 1] =dest[p]=src[p];
+ }
+
+ }
+
+ free(mask1);
+ free(mask2);
+}
+
+
diff --git a/jni/filters/redeye.c b/jni/filters/redeye.c
new file mode 100644
index 0000000..9a358dd
--- /dev/null
+++ b/jni/filters/redeye.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <math.h>
+#include "filters.h"
+
+ void JNIFUNCF(ImageFilterRedEye, nativeApplyFilter, jobject bitmap, jint width, jint height, jshortArray vrect)
+ {
+ char* destination = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+ unsigned char * rgb = (unsigned char * )destination;
+ short* rect = (*env)->GetShortArrayElements(env, vrect,0);
+
+ filterRedEye(rgb,rgb,width,height,rect);
+
+ (*env)->ReleaseShortArrayElements(env, vrect, rect, 0);
+ AndroidBitmap_unlockPixels(env, bitmap);
+ }
diff --git a/jni/filters/shadows.c b/jni/filters/shadows.c
index f812b93..38d64c8 100644
--- a/jni/filters/shadows.c
+++ b/jni/filters/shadows.c
@@ -17,23 +17,41 @@
#include <math.h>
#include "filters.h"
- void JNIFUNCF(ImageFilterShadows, nativeApplyFilter, jobject bitmap, jint width, jint height, jshortArray vlut)
- {
- char* destination = 0;
- AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
- unsigned char * rgb = (unsigned char * )destination;
- int i;
- int len = width * height * 4;
- short* lut = (*env)->GetShortArrayElements(env, vlut,0);
- unsigned short * hsv = (unsigned short *)malloc(3*sizeof(short));
- for (i = 0; i < len; i+=4)
- {
- rgb2hsv(rgb,i,hsv,0);
- hsv[0] = lut[hsv[0]];
- hsv2rgb(hsv,0, rgb,i);
- }
+void JNIFUNCF(ImageFilterShadows, nativeApplyFilter, jobject bitmap, jint width, jint height, float scale){
+ double shadowFilterMap[] = {
+ -0.00591, 0.0001,
+ 1.16488, 0.01668,
+ -0.18027, -0.06791,
+ -0.12625, 0.09001,
+ 0.15065, -0.03897
+ };
- (*env)->ReleaseShortArrayElements(env, vlut, lut, 0);
- free(hsv);
- AndroidBitmap_unlockPixels(env, bitmap);
- }
+ char* destination = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+ unsigned char * rgb = (unsigned char * )destination;
+ int i;
+ double s = (scale>=0)?scale:scale/5;
+ int len = width * height * 4;
+
+ double *poly = (double *) malloc(5*sizeof(double));
+ for (i = 0; i < 5; i++) {
+ poly[i] = fastevalPoly(shadowFilterMap+i*2,2 , s);
+ }
+
+ unsigned short * hsv = (unsigned short *)malloc(3*sizeof(short));
+
+ for (i = 0; i < len; i+=4)
+ {
+ rgb2hsv(rgb,i,hsv,0);
+
+ double v = (fastevalPoly(poly,5,hsv[0]/4080.)*4080);
+ if (v>4080) v = 4080;
+ hsv[0] = (unsigned short) ((v>0)?v:0);
+
+ hsv2rgb(hsv,0, rgb,i);
+ }
+
+ free(poly);
+ free(hsv);
+ AndroidBitmap_unlockPixels(env, bitmap);
+}