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