/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Extract histogram from image.

package androidx.media.filterpacks.colorspace;

import androidx.media.filterfw.FrameValue;

import androidx.media.filterfw.Filter;
import androidx.media.filterfw.Frame;
import androidx.media.filterfw.FrameBuffer2D;
import androidx.media.filterfw.FrameType;
import androidx.media.filterfw.MffContext;
import androidx.media.filterfw.OutputPort;
import androidx.media.filterfw.Signature;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

/**
 * ColorfulnessFilter takes in a particular Chroma histogram generated by NewChromaHistogramFilter
 * and compute the colorfulness based on the entropy in Hue space.
 */
public final class ColorfulnessFilter extends Filter {

    public ColorfulnessFilter(MffContext context, String name) {
        super(context, name);
    }

    @Override
    public Signature getSignature() {
        FrameType dataIn = FrameType.buffer2D(FrameType.ELEMENT_FLOAT32);
        return new Signature()
            .addInputPort("histogram", Signature.PORT_REQUIRED, dataIn)
            .addOutputPort("score", Signature.PORT_REQUIRED, FrameType.single(float.class))
            .disallowOtherPorts();
    }

    @Override
    protected void onProcess() {
        FrameBuffer2D histogramFrame =
                getConnectedInputPort("histogram").pullFrame().asFrameBuffer2D();
        ByteBuffer byteBuffer = histogramFrame.lockBytes(Frame.MODE_READ);
        byteBuffer.order(ByteOrder.nativeOrder());
        FloatBuffer histogramBuffer = byteBuffer.asFloatBuffer();
        histogramBuffer.rewind();

        // Create a hue histogram from hue-saturation histogram
        int hueBins = histogramFrame.getWidth();
        int saturationBins = histogramFrame.getHeight() - 1;
        float[] hueHistogram = new float[hueBins];
        float total = 0;
        for (int r = 0; r < saturationBins; ++r) {
            float weight = (float) Math.pow(2, r);
            for (int c = 0; c < hueBins; c++) {
                float value = histogramBuffer.get() * weight;
                hueHistogram[c] += value;
                total += value;
            }
        }
        float colorful = 0f;
        for (int c = 0; c < hueBins; ++c) {
            float value = hueHistogram[c] / total;
            if (value > 0f) {
                colorful -= value * ((float) Math.log(value));
            }
        }

        colorful /= Math.log(2);

        histogramFrame.unlock();
        OutputPort outPort = getConnectedOutputPort("score");
        FrameValue frameValue = outPort.fetchAvailableFrame(null).asFrameValue();
        frameValue.setValue(colorful);
        outPort.pushFrame(frameValue);
    }

}
