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