15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/*
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Copyright (C) 2012 The Android Open Source Project
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Licensed under the Apache License, Version 2.0 (the "License");
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * you may not use this file except in compliance with the License.
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * You may obtain a copy of the License at
7868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) *
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *      http://www.apache.org/licenses/LICENSE-2.0
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Unless required by applicable law or agreed to in writing, software
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * distributed under the License is distributed on an "AS IS" BASIS,
12effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * See the License for the specific language governing permissions and
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * limitations under the License.
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Extract histogram from image.
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)package androidx.media.filterpacks.histogram;
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import androidx.media.filterfw.Filter;
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import androidx.media.filterfw.Frame;
234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import androidx.media.filterfw.FrameBuffer2D;
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import androidx.media.filterfw.FrameType;
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import androidx.media.filterfw.InputPort;
264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import androidx.media.filterfw.MffContext;
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import androidx.media.filterfw.OutputPort;
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import androidx.media.filterfw.Signature;
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.nio.ByteBuffer;
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.nio.ByteOrder;
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.nio.FloatBuffer;
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * ChromaHistogramFilter takes in an image in HSVA format and computes a 2-D histogram with a
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * 2 dimensional chroma histogram based on hue (column) and saturation (row) at the top and
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * a 1-D value histogram in the last row. The number of bin in the value histogram equals to
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * the number of bins in hue.
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
40effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochpublic final class NewChromaHistogramFilter extends Filter {
41effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
42effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    private int mHueBins = 6;
434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    private int mSaturationBins = 3;
444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    private int mValueBins;
454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    private int mSaturationThreshold = 26; // 255 * 0.1
474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    private int mValueThreshold = 51; // 255 * 0.2
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    public NewChromaHistogramFilter(MffContext context, String name) {
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        super(context, name);
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    @Override
54effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    public Signature getSignature() {
55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_CPU);
56effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        FrameType dataOut = FrameType.buffer2D(FrameType.ELEMENT_FLOAT32);
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return new Signature()
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            .addInputPort("image", Signature.PORT_REQUIRED, imageIn)
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            .addInputPort("huebins", Signature.PORT_OPTIONAL, FrameType.single(int.class))
6123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)            .addInputPort("saturationbins", Signature.PORT_OPTIONAL, FrameType.single(int.class))
62effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            .addInputPort("saturationthreshold", Signature.PORT_OPTIONAL,
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    FrameType.single(int.class))
64effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            .addInputPort("valuethreshold", Signature.PORT_OPTIONAL, FrameType.single(int.class))
654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            .addOutputPort("histogram", Signature.PORT_REQUIRED, dataOut)
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            .disallowOtherPorts();
674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
69effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    @Override
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public void onInputPortOpen(InputPort port) {
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (port.getName().equals("huebins")) {
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            port.bindToFieldNamed("mHueBins");
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            port.setAutoPullEnabled(true);
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else if (port.getName().equals("saturationbins")) {
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            port.bindToFieldNamed("mSaturationBins");
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            port.setAutoPullEnabled(true);
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else if (port.getName().equals("saturationthreshold")) {
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            port.bindToFieldNamed("mSaturationThreshold");
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            port.setAutoPullEnabled(true);
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else if (port.getName().equals("valuethreshold")) {
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            port.bindToFieldNamed("mValueThreshold");
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            port.setAutoPullEnabled(true);
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Override
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    protected void onProcess() {
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        FrameBuffer2D imageFrame = getConnectedInputPort("image").pullFrame().asFrameImage2D();
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        OutputPort outPort = getConnectedOutputPort("histogram");
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        mValueBins = mHueBins;
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        int[] outDims = new int[] {mHueBins, mSaturationBins + 1};
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        FrameBuffer2D histogramFrame = outPort.fetchAvailableFrame(outDims).asFrameBuffer2D();
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ByteBuffer imageBuffer  = imageFrame.lockBytes(Frame.MODE_READ);
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        ByteBuffer histogramBuffer = histogramFrame.lockBytes(Frame.MODE_READ);
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        histogramBuffer.order(ByteOrder.nativeOrder());
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        FloatBuffer floatHistogram = histogramBuffer.asFloatBuffer();
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // Run native method
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        extractChromaHistogram(imageBuffer, floatHistogram, mHueBins, mSaturationBins, mValueBins,
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                mSaturationThreshold, mValueThreshold);
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        imageFrame.unlock();
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        histogramFrame.unlock();
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        outPort.pushFrame(histogramFrame);
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private static native void extractChromaHistogram(ByteBuffer imageBuffer,
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            FloatBuffer histogramBuffer, int hueBins, int saturationBins, int valueBins,
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            int saturationThreshold, int valueThreshold);
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    static {
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        System.loadLibrary("smartcamera_jni");
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)