Home | History | Annotate | Download | only in cannylive
      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 
     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