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