Home | History | Annotate | Download | only in filterfw
      1 /*
      2  * Copyright 2013 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 androidx.media.filterpacks.image;
     18 
     19 import androidx.media.filterfw.Filter;
     20 import androidx.media.filterfw.Frame;
     21 import androidx.media.filterfw.FrameImage2D;
     22 import androidx.media.filterfw.FrameType;
     23 import androidx.media.filterfw.ImageShader;
     24 import androidx.media.filterfw.MffContext;
     25 import androidx.media.filterfw.OutputPort;
     26 import androidx.media.filterfw.Signature;
     27 
     28 import java.nio.ByteBuffer;
     29 
     30 public class SobelFilter extends Filter {
     31 
     32     private static final String mGradientXSource =
     33               "precision mediump float;\n"
     34             + "uniform sampler2D tex_sampler_0;\n"
     35             + "uniform vec2 pix;\n"
     36             + "varying vec2 v_texcoord;\n"
     37             + "void main() {\n"
     38             + "  vec4 a1 = -1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(-pix.x, -pix.y));\n"
     39             + "  vec4 a2 = -2.0 * texture2D(tex_sampler_0, v_texcoord + vec2(-pix.x, 0.0));\n"
     40             + "  vec4 a3 = -1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(-pix.x, +pix.y));\n"
     41             + "  vec4 b1 = +1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(+pix.x, -pix.y));\n"
     42             + "  vec4 b2 = +2.0 * texture2D(tex_sampler_0, v_texcoord + vec2(+pix.x, 0.0));\n"
     43             + "  vec4 b3 = +1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(+pix.x, +pix.y));\n"
     44             + "  gl_FragColor = 0.5 + (a1 + a2 + a3 + b1 + b2 + b3) / 8.0;\n"
     45             + "}\n";
     46 
     47     private static final String mGradientYSource =
     48               "precision mediump float;\n"
     49             + "uniform sampler2D tex_sampler_0;\n"
     50             + "uniform vec2 pix;\n"
     51             + "varying vec2 v_texcoord;\n"
     52             + "void main() {\n"
     53             + "  vec4 a1 = -1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(-pix.x, -pix.y));\n"
     54             + "  vec4 a2 = -2.0 * texture2D(tex_sampler_0, v_texcoord + vec2(0.0,    -pix.y));\n"
     55             + "  vec4 a3 = -1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(+pix.x, -pix.y));\n"
     56             + "  vec4 b1 = +1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(-pix.x, +pix.y));\n"
     57             + "  vec4 b2 = +2.0 * texture2D(tex_sampler_0, v_texcoord + vec2(0.0,    +pix.y));\n"
     58             + "  vec4 b3 = +1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(+pix.x, +pix.y));\n"
     59             + "  gl_FragColor = 0.5 + (a1 + a2 + a3 + b1 + b2 + b3) / 8.0;\n"
     60             + "}\n";
     61 
     62     private static final String mMagnitudeSource =
     63             "precision mediump float;\n"
     64           + "uniform sampler2D tex_sampler_0;\n"
     65           + "uniform sampler2D tex_sampler_1;\n"
     66           + "varying vec2 v_texcoord;\n"
     67           + "void main() {\n"
     68           + "  vec4 gx = 2.0 * texture2D(tex_sampler_0, v_texcoord) - 1.0;\n"
     69           + "  vec4 gy = 2.0 * texture2D(tex_sampler_1, v_texcoord) - 1.0;\n"
     70           + "  gl_FragColor = vec4(sqrt(gx.rgb * gx.rgb + gy.rgb * gy.rgb), 1.0);\n"
     71           + "}\n";
     72 
     73     private static final String mDirectionSource =
     74             "precision mediump float;\n"
     75           + "uniform sampler2D tex_sampler_0;\n"
     76           + "uniform sampler2D tex_sampler_1;\n"
     77           + "varying vec2 v_texcoord;\n"
     78           + "void main() {\n"
     79           + "  vec4 gy = 2.0 * texture2D(tex_sampler_1, v_texcoord) - 1.0;\n"
     80           + "  vec4 gx = 2.0 * texture2D(tex_sampler_0, v_texcoord) - 1.0;\n"
     81           + "  gl_FragColor = vec4((atan(gy.rgb, gx.rgb) + 3.14) / (2.0 * 3.14), 1.0);\n"
     82           + "}\n";
     83 
     84     private ImageShader mGradientXShader;
     85     private ImageShader mGradientYShader;
     86     private ImageShader mMagnitudeShader;
     87     private ImageShader mDirectionShader;
     88 
     89     private FrameType mImageType;
     90 
     91     public SobelFilter(MffContext context, String name) {
     92         super(context, name);
     93     }
     94 
     95     @Override
     96     public Signature getSignature() {
     97         // TODO: we will address the issue of READ_GPU / WRITE_GPU when using CPU filters later.
     98         FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
     99         FrameType imageOut = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.WRITE_GPU);
    100         return new Signature().addInputPort("image", Signature.PORT_REQUIRED, imageIn)
    101                 .addOutputPort("direction", Signature.PORT_OPTIONAL, imageOut)
    102                 .addOutputPort("magnitude", Signature.PORT_OPTIONAL, imageOut).disallowOtherPorts();
    103     }
    104 
    105     @Override
    106     protected void onPrepare() {
    107         if (isOpenGLSupported()) {
    108             mGradientXShader = new ImageShader(mGradientXSource);
    109             mGradientYShader = new ImageShader(mGradientYSource);
    110             mMagnitudeShader = new ImageShader(mMagnitudeSource);
    111             mDirectionShader = new ImageShader(mDirectionSource);
    112             mImageType = FrameType.image2D(
    113                     FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU | FrameType.WRITE_GPU);
    114         }
    115     }
    116 
    117     @Override
    118     protected void onProcess() {
    119         OutputPort magnitudePort = getConnectedOutputPort("magnitude");
    120         OutputPort directionPort = getConnectedOutputPort("direction");
    121         FrameImage2D inputImage = getConnectedInputPort("image").pullFrame().asFrameImage2D();
    122         int[] inputDims = inputImage.getDimensions();
    123 
    124         FrameImage2D magImage = (magnitudePort != null) ?
    125                 magnitudePort.fetchAvailableFrame(inputDims).asFrameImage2D() : null;
    126         FrameImage2D dirImage = (directionPort != null) ?
    127                 directionPort.fetchAvailableFrame(inputDims).asFrameImage2D() : null;
    128         if (isOpenGLSupported()) {
    129             FrameImage2D gxFrame = Frame.create(mImageType, inputDims).asFrameImage2D();
    130             FrameImage2D gyFrame = Frame.create(mImageType, inputDims).asFrameImage2D();
    131             mGradientXShader.setUniformValue("pix", new float[] {1f/inputDims[0], 1f/inputDims[1]});
    132             mGradientYShader.setUniformValue("pix", new float[] {1f/inputDims[0], 1f/inputDims[1]});
    133             mGradientXShader.process(inputImage, gxFrame);
    134             mGradientYShader.process(inputImage, gyFrame);
    135             FrameImage2D[] gradientFrames = new FrameImage2D[] { gxFrame, gyFrame };
    136             if (magnitudePort != null) {
    137                 mMagnitudeShader.processMulti(gradientFrames, magImage);
    138             }
    139             if (directionPort != null) {
    140                 mDirectionShader.processMulti(gradientFrames, dirImage);
    141             }
    142             gxFrame.release();
    143             gyFrame.release();
    144         } else {
    145             ByteBuffer inputBuffer  = inputImage.lockBytes(Frame.MODE_READ);
    146             ByteBuffer magBuffer  = (magImage != null) ?
    147                     magImage.lockBytes(Frame.MODE_WRITE) : null;
    148             ByteBuffer dirBuffer  = (dirImage != null) ?
    149                     dirImage.lockBytes(Frame.MODE_WRITE) : null;
    150             sobelOperator(inputImage.getWidth(), inputImage.getHeight(),
    151                     inputBuffer, magBuffer, dirBuffer);
    152             inputImage.unlock();
    153             if (magImage != null) {
    154                 magImage.unlock();
    155             }
    156             if (dirImage != null) {
    157                 dirImage.unlock();
    158             }
    159         }
    160         if (magImage != null) {
    161             magnitudePort.pushFrame(magImage);
    162         }
    163         if (dirImage != null) {
    164             directionPort.pushFrame(dirImage);
    165         }
    166     }
    167 
    168     private static native boolean sobelOperator(int width, int height,
    169             ByteBuffer imageBuffer, ByteBuffer magBuffer, ByteBuffer dirBudder);
    170 
    171     static {
    172         System.loadLibrary("smartcamera_jni");
    173     }
    174 }
    175