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 
17 package com.android.example.cannylive;
18 
19 import android.content.ContentResolver;
20 import android.content.Context;
21 import android.graphics.Bitmap;
22 import android.graphics.BitmapFactory;
23 import android.graphics.ImageFormat;
24 import android.net.Uri;
25 import android.os.Handler;
26 import android.os.HandlerThread;
27 import android.renderscript.Allocation;
28 import android.renderscript.Element;
29 import android.renderscript.RenderScript;
30 import android.renderscript.Script;
31 import android.renderscript.Type;
32 import android.util.Log;
33 import android.util.Size;
34 import android.view.Surface;
35 
36 import java.io.FileNotFoundException;
37 import java.text.DecimalFormat;
38 
39 /**
40  * Renderscript-based Focus peaking viewfinder
41  */
42 public 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 
ViewfinderProcessor(RenderScript rs, Size dimensions)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 
getInputSurface()111     public Surface getInputSurface() {
112         return mInputAllocation.getSurface();
113     }
114 
setOutputSurface(Surface output)115     public void setOutputSurface(Surface output) {
116         mOutputAllocation.setSurface(output);
117     }
118 
getmFps()119     public float getmFps() {
120         return mFps;
121     }
122 
changeEffectMode()123     public void changeEffectMode() {
124         mMode++;
125     }
126 
getMode()127     public int getMode() {
128         return mMode;
129     }
130 
131     volatile boolean mStop = false;
132 
close()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 
ProcessingTask(Allocation input)146         public ProcessingTask(Allocation input) {
147             mInputAllocation = input;
148             mInputAllocation.setOnBufferAvailableListener(this);
149         }
150 
151         @Override
onBufferAvailable(Allocation a)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
run()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 
reProcessImage(Context context, String urlName, int type)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 
processImage(Bitmap image, Context context, int mMode)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