1dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford/* 2dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * Copyright (C) 2015 The Android Open Source Project 3dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * 4dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * Licensed under the Apache License, Version 2.0 (the "License"); 5dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * you may not use this file except in compliance with the License. 6dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * You may obtain a copy of the License at 7dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * 8dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * http://www.apache.org/licenses/LICENSE-2.0 9dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * 10dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * Unless required by applicable law or agreed to in writing, software 11dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * distributed under the License is distributed on an "AS IS" BASIS, 12dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * See the License for the specific language governing permissions and 14dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * limitations under the License. 15dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford */ 16dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 17dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordpackage rs.example.android.com.healingbrush; 18dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 19dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.graphics.Bitmap; 20dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.graphics.Canvas; 21dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.graphics.Color; 22dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.graphics.Paint; 23dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.graphics.Path; 24dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.graphics.Rect; 25dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.graphics.RectF; 26dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.support.v8.renderscript.Allocation; 27dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.support.v8.renderscript.Element; 28dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.support.v8.renderscript.RenderScript; 29dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.support.v8.renderscript.Script; 30dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.support.v8.renderscript.Type; 31dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.util.Log; 32dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 33dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport com.example.android.rs.sample.ScriptC_find_region; 34dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 35dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordpublic class FindRegion { 36dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford private static final String TAG = "FindRegion"; 37dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 38dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Rect mRoiBounds; // bounding box of the ROI 39dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int[] mPaste; // contains a copy where to paste 40dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford float[] mPointsXY; // polygon point in original image coordnates 41dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Rect mSearchRange; // range to search in (original image coordinates 42dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Bitmap mMaskBitmap; 43dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 44dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int mCutOffsetX; // image coords of the cut (mPointsXY - mPasteOffX + mCutOffsetX) 45dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int mCutOffsetY; // image coords of the cut (mPointsXY - mPasteOffY + mCutOffsetY) 46dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 47dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford public FindRegion(float[] xy, Bitmap img) { 48dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford mPointsXY = xy; 49dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 50dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int imgWidth = img.getWidth(); 51dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int imgHeight = img.getHeight(); 52dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 53dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford mRoiBounds = getBoundingRect(xy); 54dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 55dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford if (mRoiBounds.height() <= 2 || 56dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford mRoiBounds.left < 0 57dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford || mRoiBounds.top < 0 58dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford || mRoiBounds.right >= imgWidth 59dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford || mRoiBounds.bottom >= imgHeight) { 60dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford throw new RuntimeException("ROI to close to the edge of the image"); 61dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford } 62dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 63dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford mMaskBitmap = buildMask(mRoiBounds, mPointsXY); 64dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford mPaste = new int[this.mRoiBounds.width() * this.mRoiBounds.height()]; 65dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford img.getPixels(mPaste, 0, mRoiBounds.width(), 66dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford mRoiBounds.left, mRoiBounds.top, mRoiBounds.width(), mRoiBounds.height()); 67dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 68dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford mSearchRange = calcSearchRange(mRoiBounds, imgWidth, imgHeight); 69dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford } 70dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 71dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford public int getCutOffsetX() { 72dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford return mCutOffsetX; 73dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford } 74dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 75dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford public int getCutOffsetY() { 76dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford return mCutOffsetY; 77dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford } 78dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 79dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford public Rect getRoiBounds() { 80dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford return mRoiBounds; 81dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford } 82dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 83dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 84dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford public Rect findMatch(ScriptC_find_region findRegion, RenderScript mRs, Bitmap image) { 85dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford long time = System.nanoTime(); 86dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Allocation border_coords; 87dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Allocation border_values; 88dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 89fc016f378284beae5498dcfab632db1762ffe1b5I-Jui (Ray) Sung Type.Builder builderI32_2 = new Type.Builder(mRs, Element.I32_2(mRs)); 90fc016f378284beae5498dcfab632db1762ffe1b5I-Jui (Ray) Sung builderI32_2.setX(mPointsXY.length / 2); 91fc016f378284beae5498dcfab632db1762ffe1b5I-Jui (Ray) Sung border_coords = Allocation.createTyped(mRs, builderI32_2.create()); 92dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 93dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Allocation border_coords_float; 94dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford border_coords_float = Allocation.createSized(mRs, Element.F32_2(mRs), mPointsXY.length / 2); 95dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford border_coords_float.copyFrom(mPointsXY); 96dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford findRegion.forEach_toInt(border_coords_float, border_coords); 97dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford findRegion.set_border_coords(border_coords); 98dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 99dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford findRegion.set_image(Allocation.createFromBitmap(mRs, image)); 100dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 101dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Type.Builder builderF32_3 = new Type.Builder(mRs, Element.F32_3(mRs)); 102dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford builderF32_3.setX(mPointsXY.length / 2); 103dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford border_values = Allocation.createTyped(mRs, builderF32_3.create()); 104dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford findRegion.set_border_values(border_values); 105dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford findRegion.forEach_extractBorder(border_coords, border_values); 106dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 107dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Type.Builder builderF32 = new Type.Builder(mRs, Element.F32(mRs)); 108dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford builderF32.setX(mSearchRange.width()); 109dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford builderF32.setY(mSearchRange.height()); 110dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 111dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Allocation fit = Allocation.createTyped(mRs, builderF32.create()); 112dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford findRegion.set_borderLength(mPointsXY.length / 2); 113dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int noSearch_x = mRoiBounds.left - mSearchRange.left; 114dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int noSearch_y = mRoiBounds.top - mSearchRange.top; 115dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford findRegion.set_imagePosX(noSearch_x); 116dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford findRegion.set_imagePosY(noSearch_y); 117dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Script.LaunchOptions options = new Script.LaunchOptions(); 118dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford options.setX(0, mSearchRange.width() - mRoiBounds.width()); 119dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford options.setY(0, mSearchRange.height() - mRoiBounds.height()); 120dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford findRegion.forEach_bordercorrelation(fit, options); 121dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 122dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 123dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Log.v(TAG, "noSearch " + noSearch_x + ", " + noSearch_y); 124dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Log.v(TAG, "noSearch " + mRoiBounds.width() + ", " + mRoiBounds.height()); 125dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 126dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Allocation fit_max = Allocation.createSized(mRs, Element.I32_2(mRs), 1); 127dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 128dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford findRegion.invoke_findMin(fit, fit_max, noSearch_x, noSearch_y, 129dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford mRoiBounds.width(), mRoiBounds.height()); 130dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int[] mina = new int[2]; 131dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford fit_max.copyTo(mina); 132dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 133dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford mCutOffsetX = mina[0] + mSearchRange.left; 134dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford mCutOffsetY = mina[1] + mSearchRange.top; 135dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 136dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Log.v(TAG, "Time to find replacement= " + (System.nanoTime() - time) / 1E6f + "ms"); 137dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 138dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford return mRoiBounds; 139dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford } 140dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 141dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford /** 142dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * Computes the bounding box of the polygon 143dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * then pads and sizes to multiple of 8 144dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * 145dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * @param xy points of polygon [x1,y1,x2,y2,...] 146dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * @return rectangle 147dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford */ 148dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford private static Rect getBoundingRect(float[] xy) { 149dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford RectF mRect = calcBounds(xy); 150dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int mWidth = (((int) (8 + mRect.width())) & ~3); // bounding rectangle that is a power of 8 151dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int mHeight = (((int) (8 + mRect.height())) & ~3); 152dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int mPasteOffX = (int) mRect.left - 1; 153dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int mPasteOffY = (int) mRect.top - 1; 154dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford return new Rect(mPasteOffX, mPasteOffY, mPasteOffX + mWidth, mPasteOffY + mHeight); 155dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford } 156dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 157dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford private static RectF calcBounds(float[] xy) { 158dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford float minx = xy[0], miny = xy[1]; 159dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford float maxx = xy[0], maxy = xy[1]; 160dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford for (int i = 0; i < xy.length; i += 2) { 161dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford minx = Math.min(minx, xy[i]); 162dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford maxx = Math.max(maxx, xy[i]); 163dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford miny = Math.min(miny, xy[i + 1]); 164dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford maxy = Math.max(maxy, xy[i + 1]); 165dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford } 166dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford RectF rect = new RectF(); 167dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford rect.set(minx, miny, maxx, maxy); 168dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford return rect; 169dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford } 170dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 171dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford private static Bitmap buildMask(Rect rec, float[] xy) { 172dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Bitmap bitmap = Bitmap.createBitmap(rec.width(), rec.height(), Bitmap.Config.ALPHA_8); 173dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 174dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Canvas c = new Canvas(bitmap); 175dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Paint paint = new Paint(); 176dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford paint.setStyle(Paint.Style.FILL); 177dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford paint.setColor(Color.BLACK); 178dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford Path path = new Path(); 179dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford for (int i = 0; i < xy.length; i += 2) { 180dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford if (i == 0) { 181dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford path.moveTo(xy[i] - rec.left, xy[i + 1] - rec.top); 182dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford } else { 183dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford path.lineTo(xy[i] - rec.left, xy[i + 1] - rec.top); 184dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford } 185dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford } 186dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford path.close(); 187dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford c.drawPath(path, paint); 188dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford return bitmap; 189dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford } 190dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford 191dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford private static Rect calcSearchRange(Rect mRoiBounds, int imgWidth, int imgHeight) { 192dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int xmin = Math.max(0, (int) (mRoiBounds.left - mRoiBounds.width() * 2)); 193dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int ymin = Math.max(0, (int) (mRoiBounds.top - mRoiBounds.height() * 2)); 194dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int xmax = (int) (mRoiBounds.right + mRoiBounds.width() * 2); 195dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford int ymax = (int) (mRoiBounds.bottom + mRoiBounds.height() * 2); 196dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford xmax = Math.min(imgWidth, xmax); 197dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford ymax = Math.min(imgHeight, ymax); 198dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford xmax = Math.max(0, xmax); 199dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford ymax = Math.max(0, ymax); 200dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford return new Rect(xmin, ymin, xmax, ymax); 201dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford } 202dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford} 203