From 7586ddacf5d40538fd2029785eb8c53e6197eb24 Mon Sep 17 00:00:00 2001 From: John Hoford Date: Mon, 8 Oct 2012 14:21:57 -0700 Subject: add redeye and improve shadow removal bug:7234321 Change-Id: I12c2eb28555d7594fddf86dfa224219b70137681 --- jni/Android.mk | 4 +- jni/filters/filters.h | 3 +- jni/filters/hsv.c | 240 +++++++++++++++++++++++++---------------------- jni/filters/redEyeMath.c | 172 +++++++++++++++++++++++++++++++++ jni/filters/redeye.c | 31 ++++++ jni/filters/shadows.c | 58 ++++++++---- 6 files changed, 372 insertions(+), 136 deletions(-) create mode 100644 jni/filters/redEyeMath.c create mode 100644 jni/filters/redeye.c (limited to 'jni') diff --git a/jni/Android.mk b/jni/Android.mk index 213663ce1..92789c157 100644 --- a/jni/Android.mk +++ b/jni/Android.mk @@ -34,7 +34,9 @@ LOCAL_SRC_FILES := filters/bw.c \ 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 954d02ddd..d8728f0d4 100644 --- a/jni/filters/filters.h +++ b/jni/filters/filters.h @@ -47,5 +47,6 @@ __inline__ unsigned char clamp(int c); 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 af438f87a..aabd053fe 100644 --- a/jni/filters/hsv.c +++ b/jni/filters/hsv.c @@ -17,128 +17,140 @@ #include #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 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; - - 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) + int iMin,iMax,chroma; + int ABITS = 4; + int HSCALE = 256; + + 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; + + 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 + else rs = (short)((k1*chroma)/iMax); - // set hue - if (rs == 0) - rh = 0; - else { - if ( ri == 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) + } 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; + else // (bi == iMax ) + rh = (short)( (k2*(4*chroma+ri - gi ))/(6*chroma)); } + hsv[hsvOff+0] = rv; + hsv[hsvOff+1] = rs; + hsv[hsvOff+2] = rh; +} + +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< 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; - } + // 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 000000000..26f3f76a4 --- /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 +#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 000000000..9a358dd3d --- /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 +#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 f812b93f8..38d64c8b5 100644 --- a/jni/filters/shadows.c +++ b/jni/filters/shadows.c @@ -17,23 +17,41 @@ #include #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); - } - - (*env)->ReleaseShortArrayElements(env, vlut, lut, 0); - free(hsv); - AndroidBitmap_unlockPixels(env, bitmap); - } +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 + }; + + 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); +} -- cgit v1.2.3