All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.simiacryptus.mindseye.art.util.PCA Maven / Gradle / Ivy

/*
 * Copyright (c) 2019 by Andrew Charneski.
 *
 * The author licenses this file to you 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.
 */

package com.simiacryptus.mindseye.art.util;

import com.simiacryptus.mindseye.lang.Result;
import com.simiacryptus.mindseye.lang.Tensor;
import com.simiacryptus.mindseye.layers.cudnn.BandReducerLayer;
import com.simiacryptus.mindseye.layers.cudnn.ImgBandBiasLayer;
import com.simiacryptus.mindseye.layers.cudnn.PoolingLayer;
import com.simiacryptus.mindseye.layers.cudnn.SquareActivationLayer;
import com.simiacryptus.mindseye.layers.java.NthPowerActivationLayer;
import com.simiacryptus.mindseye.network.PipelineNetwork;
import com.simiacryptus.ref.lang.RecycleBin;
import com.simiacryptus.ref.lang.RefUtil;
import com.simiacryptus.ref.wrappers.*;
import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.EigenDecomposition;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class PCA {
  private boolean recenter;
  private boolean rescale;
  private double eigenvaluePower;

  public PCA(boolean recenter, boolean rescale, double eigenvaluePower) {
    this.setRecenter(recenter);
    this.setRescale(rescale);
    this.setEigenvaluePower(eigenvaluePower);
  }

  public PCA() {
    this(true, false, 0.0);
  }

  public double getEigenvaluePower() {
    return eigenvaluePower;
  }

  @Nonnull
  public PCA setEigenvaluePower(double eigenvaluePower) {
    this.eigenvaluePower = eigenvaluePower;
    return this;
  }

  public boolean isRecenter() {
    return recenter;
  }

  @Nonnull
  public PCA setRecenter(boolean recenter) {
    this.recenter = recenter;
    return this;
  }

  public boolean isRescale() {
    return rescale;
  }

  @Nonnull
  public PCA setRescale(boolean rescale) {
    this.rescale = rescale;
    return this;
  }

  public static double[] bandCovariance(@Nonnull final RefStream pixelStream, final int pixels, final double[] mean,
                                        final double[] rms) {
    return RefArrays.stream(RefUtil.get(pixelStream.map(pixel -> {
      double[] crossproduct = RecycleBin.DOUBLES.obtain(pixel.length * pixel.length);
      int k = 0;
      for (int j = 0; j < pixel.length; j++) {
        for (int i = 0; i < pixel.length; i++) {
          crossproduct[k++] = (pixel[i] - mean[i]) / (rms[i] == 0 ? 1 : rms[i])
              * ((pixel[j] - mean[j]) / (rms[j] == 0 ? 1 : rms[j]));
        }
      }
      RecycleBin.DOUBLES.recycle(pixel, pixel.length);
      return crossproduct;
    }).reduce((a, b) -> {
      for (int i = 0; i < a.length; i++) {
        a[i] += b[i];
      }
      RecycleBin.DOUBLES.recycle(b, b.length);
      return a;
    }))).map(x -> x / pixels).toArray();
  }

  public static int countPixels(@Nonnull final Tensor featureImage) {
    int[] dimensions = featureImage.getDimensions();
    featureImage.freeRef();
    int width = dimensions[0];
    int height = dimensions[1];
    return width * height;
  }

  public static RefList pca(@Nonnull final double[] bandCovariance, final double eigenPower) {
    @Nonnull final EigenDecomposition decomposition = new EigenDecomposition(toMatrix(bandCovariance));
    return RefIntStream.range(0, (int) Math.sqrt(bandCovariance.length)).mapToObj(vectorIndex -> {
      double[] data = decomposition.getEigenvector(vectorIndex).toArray();
      Tensor tensor = new Tensor(data, 1, 1, data.length).unit();
      tensor.scaleInPlace(Math.pow(decomposition.getRealEigenvalue(vectorIndex), eigenPower));
      return tensor;
    }).collect(RefCollectors.toList());
  }

  @Nonnull
  private static Array2DRowRealMatrix toMatrix(@Nonnull final double[] covariance) {
    final int bands = (int) Math.sqrt(covariance.length);
    Array2DRowRealMatrix matrix = new Array2DRowRealMatrix(bands, bands);
    int k = 0;
    for (int x = 0; x < bands; x++) {
      for (int y = 0; y < bands; y++) {
        matrix.setEntry(x, y, covariance[k++]);
      }
    }
    return matrix;
  }

  public RefList channelPCA(@Nonnull Tensor image) {
    Tensor meanTensor = getChannelMeans(image.addRef());
    Tensor scaled = getChannelRms(image.addRef(), image.getDimensions()[2], meanTensor.addRef());
    assert scaled != null;
    double[] bandCovariance = bandCovariance(image.getPixelStream(), countPixels(image.addRef()), meanTensor.getData(),
        scaled.getData());
    image.freeRef();
    meanTensor.freeRef();
    scaled.freeRef();
    return pca(bandCovariance, getEigenvaluePower());
  }

  @Nullable
  public Tensor getChannelRms(Tensor image, int bands, @Nonnull Tensor meanTensor) {
    if (!isRescale()) {
      Tensor tensor = meanTensor.map(x -> 1);
      meanTensor.freeRef();
      image.freeRef();
      return tensor;
    } else {
      NthPowerActivationLayer nthPowerActivationLayer = new NthPowerActivationLayer();
      nthPowerActivationLayer.setPower(0.5);
      BandReducerLayer bandReducerLayer = new BandReducerLayer();
      bandReducerLayer.setMode(PoolingLayer.PoolingMode.Avg);
      ImgBandBiasLayer imgBandBiasLayer = new ImgBandBiasLayer(bands);
      imgBandBiasLayer.set(meanTensor.scale(-1));
      meanTensor.freeRef();
      PipelineNetwork network = PipelineNetwork.build(1,
          imgBandBiasLayer,
          new SquareActivationLayer(),
          bandReducerLayer,
          nthPowerActivationLayer
      );
      try {
        Tensor data0 = Result.getData0(network.eval(image));
        Tensor map = data0.map(x -> x == 0.0 ? 1.0 : x);
        data0.freeRef();
        return map;
      } finally {
        network.freeRef();
      }
    }
  }

  @Nonnull
  public Tensor getChannelMeans(Tensor image) {
    BandReducerLayer bandReducerLayer = new BandReducerLayer();
    bandReducerLayer.setMode(PoolingLayer.PoolingMode.Avg);
    Tensor meanTensor = Result.getData0(bandReducerLayer.eval(image));
    bandReducerLayer.freeRef();
    if (!isRecenter())
      meanTensor.fill(0);
    return meanTensor;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy