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 com.android.example.cannylive;
18
19import android.content.ContentResolver;
20import android.content.Context;
21import android.graphics.Bitmap;
22import android.graphics.BitmapFactory;
23import android.graphics.ImageFormat;
24import android.net.Uri;
25import android.os.Handler;
26import android.os.HandlerThread;
27import android.renderscript.Allocation;
28import android.renderscript.Element;
29import android.renderscript.RenderScript;
30import android.renderscript.Script;
31import android.renderscript.Type;
32import android.util.Log;
33import android.util.Size;
34import android.view.Surface;
35
36import java.io.FileNotFoundException;
37import java.text.DecimalFormat;
38
39/**
40 * Renderscript-based Focus peaking viewfinder
41 */
42public class ViewfinderProcessor {
43    private static final String TAG = "ViewfinderProcessor";
44    int mCount;
45    long mLastTime;
46    float mFps;
47    RenderScript mRs;
48    private Allocation mInputAllocation;
49    private Allocation mOutputAllocation;
50    private Allocation mBlurAllocation;
51    private Allocation mEdgeAllocation;
52    private HandlerThread mProcessingThread;
53    private Handler mProcessingHandler;
54    private ScriptC_canny mScriptCanny;
55    public ProcessingTask mProcessingTask;
56    public Allocation mHoughOutput;
57    public Allocation mHoughSlices;
58    private volatile int mMode = 1;
59    DecimalFormat df = new DecimalFormat("###.##");
60
61    public ViewfinderProcessor(RenderScript rs, Size dimensions) {
62        mRs = rs;
63        Type.Builder yuvTypeBuilder = new Type.Builder(rs, Element.YUV(rs));
64        yuvTypeBuilder.setX(dimensions.getWidth());
65        yuvTypeBuilder.setY(dimensions.getHeight());
66        yuvTypeBuilder.setYuvFormat(ImageFormat.YUV_420_888);
67        Log.d(TAG, ">>>>>>>>>>>>  " + dimensions.getWidth() + "x" + dimensions.getHeight());
68        mInputAllocation = Allocation.createTyped(rs, yuvTypeBuilder.create(),
69                Allocation.USAGE_IO_INPUT | Allocation.USAGE_SCRIPT);
70
71        Type.Builder rgbTypeBuilder = new Type.Builder(rs, Element.RGBA_8888(rs));
72        rgbTypeBuilder.setX(dimensions.getWidth());
73        rgbTypeBuilder.setY(dimensions.getHeight());
74
75        mOutputAllocation = Allocation.createTyped(rs, rgbTypeBuilder.create(),
76                Allocation.USAGE_IO_OUTPUT | Allocation.USAGE_SCRIPT);
77        Type.Builder buffTypeBuilder = new Type.Builder(rs, Element.U8(rs));
78        buffTypeBuilder.setX(dimensions.getWidth());
79        buffTypeBuilder.setY(dimensions.getHeight());
80        mBlurAllocation = Allocation.createTyped(rs, buffTypeBuilder.create());
81        mEdgeAllocation = Allocation.createTyped(rs, buffTypeBuilder.create());
82
83        mProcessingThread = new HandlerThread("ViewfinderProcessor");
84        mProcessingThread.start();
85        mProcessingHandler = new Handler(mProcessingThread.getLooper());
86        mScriptCanny = new ScriptC_canny(rs);
87        mScriptCanny.set_blurImage(mBlurAllocation);
88        mScriptCanny.set_edgeImage(mEdgeAllocation);
89        mProcessingTask = new ProcessingTask(mInputAllocation);
90
91        int NO_OF_SLICES = 8;
92        int[] slices = new int[NO_OF_SLICES * 2];
93        for (int i = 0; i < NO_OF_SLICES; i++) {
94            int s1 = i * 360 / NO_OF_SLICES;
95            int s2 = ((1 + i) * 360) / NO_OF_SLICES;
96            slices[i * 2] = s1;
97            slices[i * 2 + 1] = s2;
98        }
99        Type.Builder houghSliceBuilder = new Type.Builder(rs, Element.I32_2(rs));
100        houghSliceBuilder.setX(NO_OF_SLICES);
101        mHoughSlices = Allocation.createTyped(rs, houghSliceBuilder.create(), Allocation.USAGE_SCRIPT);
102        mHoughSlices.copyFrom(slices);
103        Type.Builder houghOutputBuilder = new Type.Builder(rs, Element.U8(rs));
104        houghOutputBuilder.setX(800);
105        houghOutputBuilder.setY(360);
106        mHoughOutput = Allocation.createTyped(rs, houghOutputBuilder.create());
107        mScriptCanny.set_hough_output(mHoughOutput);
108
109    }
110
111    public Surface getInputSurface() {
112        return mInputAllocation.getSurface();
113    }
114
115    public void setOutputSurface(Surface output) {
116        mOutputAllocation.setSurface(output);
117    }
118
119    public float getmFps() {
120        return mFps;
121    }
122
123    public void changeEffectMode() {
124        mMode++;
125    }
126
127    public int getMode() {
128        return mMode;
129    }
130
131    volatile boolean mStop = false;
132
133    public void close() {
134
135        mStop = true;
136    }
137
138    /**
139     * Class to process buffer from camera and output to buffer to screen
140     */
141    class ProcessingTask implements Runnable, Allocation.OnBufferAvailableListener {
142        private int mPendingFrames = 0;
143        int mode = -1;
144        private Allocation mInputAllocation;
145
146        public ProcessingTask(Allocation input) {
147            mInputAllocation = input;
148            mInputAllocation.setOnBufferAvailableListener(this);
149        }
150
151        @Override
152        public void onBufferAvailable(Allocation a) {
153            if (mStop) {
154
155                return;
156            }
157            synchronized (this) {
158                mPendingFrames++;
159                mProcessingHandler.post(this);
160            }
161        }
162
163        @Override
164        public void run() {
165            // Find out how many frames have arrived
166            int pendingFrames;
167            synchronized (this) {
168                pendingFrames = mPendingFrames;
169                mPendingFrames = 0;
170
171                // Discard extra messages in case processing is slower than frame rate
172                mProcessingHandler.removeCallbacks(this);
173            }
174            if (mInputAllocation == null) return;
175            // Get to newest input
176            for (int i = 0; i < pendingFrames; i++) {
177                mInputAllocation.ioReceive();
178            }
179            mCount++;
180            mScriptCanny.set_gCurrentFrame(mInputAllocation);
181            long time = System.currentTimeMillis() - mLastTime;
182            if (time > 1000) {
183                mLastTime += time;
184                mFps = mCount * 1000 / (float) (time);
185                mCount = 0;
186            }
187            // Run processing pass
188            mScriptCanny.forEach_getyuv_y(mEdgeAllocation);
189
190            Script.LaunchOptions opt = new Script.LaunchOptions();
191            opt.setX(2, mBlurAllocation.getType().getX() - 2);
192            opt.setY(2, mBlurAllocation.getType().getY() - 2);
193            mScriptCanny.forEach_blur_uchar(mBlurAllocation, opt);
194
195            opt.setX(3, mBlurAllocation.getType().getX() - 3);
196            opt.setY(3, mBlurAllocation.getType().getY() - 3);
197            mScriptCanny.forEach_edge(mEdgeAllocation, opt);
198
199            opt.setX(4, mBlurAllocation.getType().getX() - 4);
200            opt.setY(4, mBlurAllocation.getType().getY() - 4);
201            mScriptCanny.forEach_thin(mBlurAllocation, opt);
202
203            opt.setX(5, mBlurAllocation.getType().getX() - 5);
204            opt.setY(5, mBlurAllocation.getType().getY() - 5);
205            mScriptCanny.forEach_hysteresis(mBlurAllocation, mEdgeAllocation, opt);
206
207            switch (mMode % 6) {
208                case 0:
209                default:
210                    long mt = System.nanoTime();
211                    mScriptCanny.forEach_black_uchar(mHoughOutput);
212                    mScriptCanny.forEach_hough(mHoughSlices);
213                    mRs.finish();
214                    mt = System.nanoTime() - mt;
215                    Log.v(TAG, " hough = " + df.format(mt * 1E-6) + "ms");
216                    mScriptCanny.forEach_hough_map(mOutputAllocation);
217                    break;
218                case 1:
219                    mScriptCanny.forEach_toRGB(mOutputAllocation, opt);
220                    break;
221                case 2:
222                    mScriptCanny.forEach_toRGBfuzz(mOutputAllocation, opt);
223                    break;
224                case 3:
225                    mScriptCanny.forEach_toWhiteRGBfuzz(mOutputAllocation, opt);
226                    break;
227                case 4:
228                    mScriptCanny.forEach_toWhiteRGB(mOutputAllocation, opt);
229                    break;
230                case 5:
231                    mScriptCanny.forEach_toCartoon(mOutputAllocation, opt);
232                    break;
233            }
234            mOutputAllocation.ioSend();
235            if (mStop) {
236                if (mInputAllocation != null) {
237                    mInputAllocation.destroy();
238                    mInputAllocation = null;
239                }
240                return;
241            }
242        }
243    }
244
245
246    public static void reProcessImage(Context context, String urlName, int type) {
247
248        ContentResolver cr = context.getContentResolver();
249        try {
250            Uri uri = Uri.parse(urlName);
251            Bitmap b = BitmapFactory.decodeStream(cr.openInputStream(uri));
252            processImage(b, context, type);
253
254            MediaStoreSaver.insertImage(cr, b, "canny", "canny filtered image");
255        } catch (FileNotFoundException e) {
256            Log.v(TAG, "S>> Could not open file ");
257        }
258
259    }
260
261    public static void processImage(Bitmap image, Context context, int mMode) {
262        RenderScript mRs = RenderScript.create(context);
263        int width = image.getWidth();
264        int height = image.getHeight();
265        Allocation img_alloc, blur_alloc, edge_alloc;
266        long time = System.nanoTime();
267        img_alloc = Allocation.createFromBitmap(mRs, image);
268
269        Type.Builder buffTypeBuilder = new Type.Builder(mRs, Element.U8(mRs));
270        buffTypeBuilder.setX(width).setY(height);
271        blur_alloc = Allocation.createTyped(mRs, buffTypeBuilder.create());
272        edge_alloc = Allocation.createTyped(mRs, buffTypeBuilder.create());
273
274
275        ScriptC_canny canny_script = new ScriptC_canny(mRs);
276        canny_script.set_blurImage(blur_alloc);
277        canny_script.set_edgeImage(edge_alloc);
278        canny_script.forEach_getLum(img_alloc, edge_alloc);
279
280        Script.LaunchOptions opt = new Script.LaunchOptions();
281        opt.setX(2, blur_alloc.getType().getX() - 2);
282        opt.setY(2, blur_alloc.getType().getY() - 2);
283        canny_script.forEach_blur_uchar(blur_alloc, opt);
284
285        opt.setX(3, blur_alloc.getType().getX() - 3);
286        opt.setY(3, blur_alloc.getType().getY() - 3);
287        canny_script.forEach_edge(edge_alloc, opt);
288
289        opt.setX(4, blur_alloc.getType().getX() - 4);
290        opt.setY(4, blur_alloc.getType().getY() - 4);
291        canny_script.forEach_thin(blur_alloc, opt);
292
293        opt.setX(5, blur_alloc.getType().getX() - 5);
294        opt.setY(5, blur_alloc.getType().getY() - 5);
295
296        canny_script.forEach_hysteresis(blur_alloc, edge_alloc, opt);
297        switch (mMode % 6) {
298            case 0:
299            case 1:
300            default:
301                canny_script.forEach_toRGB(img_alloc, opt);
302                break;
303            case 2:
304                canny_script.forEach_toRGBfuzz(img_alloc, opt);
305                break;
306            case 3:
307                canny_script.forEach_toWhiteRGBfuzz(img_alloc, opt);
308                break;
309            case 4:
310                canny_script.forEach_toWhiteRGB(img_alloc, opt);
311                break;
312            case 5:
313                canny_script.forEach_toRGBCartoon(img_alloc, img_alloc, opt);
314                break;
315        }
316        img_alloc.copyTo(image);
317        time = System.nanoTime() - time;
318        DecimalFormat df = new DecimalFormat("###.#");
319        String ts = df.format(time * 1E-6) + "ms";
320        Log.v(TAG, "processed a " + width + "x" + height + " in " + ts);
321    }
322
323}
324