Fine tune the circle detection

Decreased the pixel_diff_percentage from 10% to 0.5% by
not counting the pixel_diff around the mask path outline.

Verified that this implementation works well on both Pixel/PixelXL

b/36563706

Change-Id: I8bb488f550a67e2188eff4c4b9e7717e979a741c
This commit is contained in:
Hyunyoung Song
2017-03-28 18:43:56 -07:00
parent a0b4ac56c1
commit ffd7e76261
2 changed files with 26 additions and 12 deletions
+1 -1
View File
@@ -763,7 +763,7 @@ public class IconCache {
}
private static final class IconDB extends SQLiteCacheHelper {
private final static int DB_VERSION = 10;
private final static int DB_VERSION = 11;
private final static int RELEASE_VERSION = DB_VERSION +
(FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
@@ -60,7 +60,7 @@ public class IconNormalizer {
// Shape detection related constants
private static final float BOUND_RATIO_MARGIN = .05f;
private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.1f;
private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f;
private static final float SCALE_NOT_INITIALIZED = 0;
private static final Object LOCK = new Object();
@@ -71,6 +71,7 @@ public class IconNormalizer {
private final Bitmap mBitmapARGB;
private final Canvas mCanvas;
private final Paint mPaintMaskShape;
private final Paint mPaintMaskShapeOutline;
private final byte[] mPixels;
private final int[] mPixelsARGB;
private float mAdaptiveIconScale;
@@ -79,14 +80,15 @@ public class IconNormalizer {
private final float[] mLeftBorder;
private final float[] mRightBorder;
private final Rect mBounds;
private final RectF mShapeBounds;
private final Matrix mMatrix;
private Paint mPaintIcon;
private Canvas mCanvasARGB;
private File mDir;
private int mFileId;
private Random mRandom;
private float mDensity;
private IconNormalizer(Context context) {
// Use twice the icon size as maximum size to avoid scaling down twice.
@@ -98,17 +100,25 @@ public class IconNormalizer {
mLeftBorder = new float[mMaxSize];
mRightBorder = new float[mMaxSize];
mBounds = new Rect();
mDensity = context.getResources().getDisplayMetrics().density;
// Needed for isShape() method
mBitmapARGB = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ARGB_8888);
mCanvasARGB = new Canvas(mBitmapARGB);
mShapeBounds = new RectF();
mPaintIcon = new Paint();
mPaintIcon.setColor(Color.WHITE);
mPaintMaskShape = new Paint();
mPaintMaskShape.setColor(Color.RED);
mPaintMaskShape.setStyle(Paint.Style.FILL);
mPaintMaskShape.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
mPaintMaskShapeOutline = new Paint();
mPaintMaskShapeOutline.setStrokeWidth(2 * mDensity);
mPaintMaskShapeOutline.setStyle(Paint.Style.STROKE);
mPaintMaskShapeOutline.setColor(Color.BLACK);
mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
mMatrix = new Matrix();
int[] mPixels = new int[mMaxSize * mMaxSize];
mAdaptiveIconScale = SCALE_NOT_INITIALIZED;
@@ -136,12 +146,12 @@ public class IconNormalizer {
// Condition 2:
// Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation
// should generate transparent image, if the actual icon is equivalent to the shape.
int id = mRandom.nextInt();
mFileId = mRandom.nextInt();
mBitmapARGB.eraseColor(Color.TRANSPARENT);
mCanvasARGB.drawBitmap(mBitmap, 0, 0, mPaintIcon);
if (DEBUG) {
final File beforeFile = new File(mDir, "isShape" + id + "_before.png");
final File beforeFile = new File(mDir, "isShape" + mFileId + "_before.png");
try {
mBitmapARGB.compress(Bitmap.CompressFormat.PNG, 100,
new FileOutputStream(beforeFile));
@@ -149,24 +159,28 @@ public class IconNormalizer {
}
// Fit the shape within the icon's bounding box
mMatrix.reset();
mMatrix.setScale(mBounds.width(), mBounds.height());
mMatrix.postTranslate(mBounds.left, mBounds.top);
maskPath.transform(mMatrix);
maskPath.computeBounds(mShapeBounds, false);
// XOR operation
mCanvasARGB.drawPath(maskPath, mPaintMaskShape);
// DST_OUT operation around the mask path outline
mCanvasARGB.drawPath(maskPath, mPaintMaskShapeOutline);
boolean isTrans = isTransparentBitmap(mBitmapARGB);
if (DEBUG) {
final File afterFile = new File(mDir, "isShape" + id + "_after.png");
final File afterFile = new File(mDir, "isShape" + mFileId + "_after_" + isTrans + ".png");
try {
mBitmapARGB.compress(Bitmap.CompressFormat.PNG, 100,
new FileOutputStream(afterFile));
} catch (Exception e) {}
}
// Check if the XOR operation result is almost transparent
if (!isTransparentBitmap(mBitmapARGB)) {
// Check if the result is almost transparent
if (!isTrans) {
if (DEBUG) {
Log.d(TAG, "Not same as mask shape");
}
@@ -194,7 +208,7 @@ public class IconNormalizer {
float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height());
boolean transparentImage = percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD;
if (DEBUG) {
Log.d(TAG, "Total # pixel that is different:" + percentageDiffPixels + "="+ sum + "/" + mBounds.width() * mBounds.height());
Log.d(TAG, "Total # pixel that is different (id="+ mFileId + "):" + percentageDiffPixels + "="+ sum + "/" + mBounds.width() * mBounds.height());
}
return transparentImage;
}