I was researching on how to calculate an Optimal Threshold for ImageJ and found this explanation of the Otsu Thresholding, which I thought made perfect sense for me to use.
I've struggled with implementing it though, and after some thought I found a mistake with the way the weight and mean were calculated, and it now finds the optimal threshold of 77, which for the coin image looks good to me since it almost completely separates the background from the coins (and you'd be able to automatically count the coins, or measure them for size, ectr)
new coin image with optimal threshold
it also seems to work pretty well with this image, even though it has varying intensities of light:
rice image with varying intensities
I'm quite happy with my solution found, but if you have any feedback or can find something else, that'd be great! This homework was tough but I learned a lot from it :)
public float calculateMeanFG(int[] histogram, int t) {
float sumI = 0;
int total = 0;
//cumulate the histogram for < 256
for (int i = t; i < 256; i++) {
sumI += histogram[i] * i;
total = i;
}
return sumI / total;
}
public float calculateMeanBG(int[] histogram, int t) {
float sumI = 0;
//cumulate the histogram for < t
for (int i = 0; i < t; i++) {
sumI += histogram[i] * i;
}
return sumI;
}
public float calculateWeightFG(int[] histogram, int t, int total) {
int sum = 0;
for (int i = t; i < 256; i++) {
sum += histogram[i];
}
return sum / total;
}
public int[] getHistogram(ImageProcessor ip, int height, int width) {
byte[] outP = ((byte[]) ip.getPixels()).clone();
int[][] inDataArr = new int[width][height];
int[] histogram = new int[256];
int idx = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// fill in values
inDataArr[x][y] = outP[idx];
if (inDataArr[x][y] < 0) {
inDataArr[x][y] += 256;
} // if
histogram[inDataArr[x][y]] += 1; // count grayscale occurrences
idx++;
} // for x
} // for y
return histogram;
}
public int[][] convergeOptThresh(int[][] imgArr, int width, int height) {
int BG_VAL = 0;
int FG_VAL = 255;
int[] histogram = getHistogram(ip, height, width);
// total number of pixels
int total = imgArr.length;
// cumulative hist
float sum = 0;
for (int i = 0; i < 256; i++)
sum += i * histogram[i];
float sumBG = 0; // sum background
float weightBG = 0;
float weightFG = 0;
float varMax = 0;
int threshold = 0;
for (int t = 0; t < 256; t++) {
weightBG = calculateMeanBG(histogram, t);
weightBG /= total;
weightFG = calculateWeightFG(histogram, t, total);
if ((int)weightFG == 0)
break;
sumBG += (float) (t * histogram[t]);
float meanBG = sumBG / t;
float meanFG = calculateMeanFG(histogram, t);
// calculate between class variance
float varBetween = weightBG * weightFG * (meanBG - meanFG) * (meanBG - meanFG);
// check if new max found
if (varBetween > varMax) {
varMax = varBetween;
threshold = t;
}
}
IJ.log("optimal threshold: " + threshold);
int[][] retArr = new int[width][height];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (imgArr[x][y] <= threshold) {
retArr[x][y] = BG_VAL;
} else {
retArr[x][y] = FG_VAL;
}
}
}
return retArr;
}
not sure if this is what you meant? sorry- still new to SO >.<
public float calculateMeanFG(int[] histogram, int t) {
float sumI = 0;
int total = 0;
//cumulate the histogram for < 256
for (int i = t; i < 256; i++) {
sumI += histogram[i] * i;
total = i;
}
return sumI / total;
}
public float calculateMeanBG(int[] histogram, int t) {
float sumI = 0;
//cumulate the histogram for < t
for (int i = 0; i < t; i++) {
sumI += histogram[i] * i;
}
return sumI;
}
public float calculateWeightFG(int[] histogram, int t, int total) {
int sum = 0;
for (int i = t; i < 256; i++) {
sum += histogram[i];
}
return sum / total;
}
public int[] getHistogram(ImageProcessor ip, int height, int width) {
byte[] outP = ((byte[]) ip.getPixels()).clone();
int[][] inDataArr = new int[width][height];
int[] histogram = new int[256];
int idx = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// fill in values
inDataArr[x][y] = outP[idx];
if (inDataArr[x][y] < 0) {
inDataArr[x][y] += 256;
} // if
histogram[inDataArr[x][y]] += 1; // count grayscale occurrences
idx++;
} // for x
} // for y
return histogram;
}
public int[][] convergeOptThresh(int[][] imgArr, int width, int height) {
int BG_VAL = 0;
int FG_VAL = 255;
int[] histogram = getHistogram(ip, height, width);
// total number of pixels
int total = imgArr.length;
// cumulative hist
float sum = 0;
for (int i = 0; i < 256; i++)
sum += i * histogram[i];
float sumBG = 0; // sum background
float weightBG = 0;
float weightFG = 0;
float varMax = 0;
int threshold = 0;
for (int t = 0; t < 256; t++) {
weightBG = calculateMeanBG(histogram, t);
weightBG /= total;
weightFG = calculateWeightFG(histogram, t, total);
if ((int)weightFG == 0)
break;
sumBG += (float) (t * histogram[t]);
float meanBG = sumBG / t;
float meanFG = calculateMeanFG(histogram, t);
// calculate between class variance
float varBetween = weightBG * weightFG * (meanBG - meanFG) * (meanBG - meanFG);
// check if new max found
if (varBetween > varMax) {
varMax = varBetween;
threshold = t;
}
}
IJ.log("optimal threshold: " + threshold);
int[][] retArr = new int[width][height];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (imgArr[x][y] <= threshold) {
retArr[x][y] = BG_VAL;
} else {
retArr[x][y] = FG_VAL;
}
}
}
return retArr;
}
Related
Im currently trying to implement SAT in java but for some reason it doesn't work. I have rewritten my code multiple times, looked over it even more and watched many tutorials but cant find my mistake. In some cases for some edges it works partly properly but otherwise it detects collision when not colliding.
Later i'll add AABB collision detection for better performance.
Here are the relevant parts of my code:
SAT class:
public class SAT {
public static boolean checkSAT(Polygon poly1, Polygon poly2) {
Vector[] axes = new Vector[poly1.p.length + poly2.p.length];
for (int i = 0; i < poly1.p.length + poly2.p.length; i++) {
int a = i; if(i == poly1.p.length) a -= poly1.p.length;
axes[i] = poly1.getEdge(a).getNormal().getNormalized();
}
double p1_min = Double.POSITIVE_INFINITY, p1_max = Double.NEGATIVE_INFINITY,
p2_min = Double.POSITIVE_INFINITY, p2_max = Double.NEGATIVE_INFINITY;
for (int i = 0; i < axes.length; i++) {
for (int j = 0; j < poly1.p.length; j++) {
double proj = axes[i].dotProduct(poly1.p[j]);
if(proj < p1_min) p1_min = proj;
if(proj > p1_max) p1_max = proj;
}
for (int j = 0; j < poly2.p.length; j++) {
double proj = axes[i].dotProduct(poly2.p[j]);
if(proj < p2_min) p2_min = proj;
if(proj > p2_max) p2_max = proj;
}
if (p1_max < p2_min || p2_max < p1_min)
return false;
}
return true;
}
}
vector class:
public class Vector {
public final double x;
public final double y;
public Vector(double x, double y) {
this.x = x;
this.y = y;
}
public Vector getNormal() {
return new Vector(-y, x);
}
public double getLength() {
return Math.sqrt(x*x + y*y);
}
public Vector getNormalized() {
double l = getLength();
return new Vector(x/l, y/l);
}
public double dotProduct(Vector vec) {
return x * vec.x + y * vec.y;
}
}
relevant parts of the polygon class:
public class Polygon {
public Vector[] m; //"model" of the polygon
public Vector[] p; //coordinates of the corners of the polygon in space
public double posX;
public double posY;
public Polygon(Vector[] m) {
this.m = m;
p = new Vector[m.length];
transform();
}
//later i'll add rotation
public void transform() {
for (int i = 0; i < m.length; i++) {
p[i] = new Vector(m[i].x + posX, m[i].y + posY);
}
}
public void setPosition(Vector pos) {
posX = pos.x;
posY = pos.y;
transform();
}
public Vector getEdge(int i) {
if(i >= p.length) i = 0;
int j = i+1; if(j >= p.length) j = 0;
return new Vector(p[j].x - p[i].x, p[j].y - p[i].y);
}
}
Update:
I found the mistake and it's just plain stupid!! And on top of that I spend more than 5 hours finding it!!!!!!
double p1_min = Double.POSITIVE_INFINITY, p1_max = Double.NEGATIVE_INFINITY,
p2_min = Double.POSITIVE_INFINITY, p2_max = Double.NEGATIVE_INFINITY;
//those doubles should be declared inside the for loop
for (int i = 0; i < axes.length; i++) {
//right here
for (int j = 0; j < poly1.p.length; j++) {
double proj = axes[i].dotProduct(poly1.p[j]);
if(proj < p1_min) p1_min = proj;
if(proj > p1_max) p1_max = proj;
}
for (int j = 0; j < poly2.p.length; j++) {
double proj = axes[i].dotProduct(poly2.p[j]);
if(proj < p2_min) p2_min = proj;
if(proj > p2_max) p2_max = proj;
}
if (p1_max < p2_min || p2_max < p1_min)
return false;
}
I have implemented a simple neural network in Java that is supposed to solve the XOR problem (http://www.mind.ilstu.edu/curriculum/artificial_neural_net/xor_problem_and_solution.php).
I was wondering what you need Neuron and a NeuronLayer etc. classes for, so I decided to try to make it work using only one class.
Unfortunately, after some epochs, it reaches a state where the error is one of two values: 0.5 or -0.5.
I have tried limiting the range that the starting weights are chosen from but that didn't change anything.
Maybe, some of you can help me spot a mistake or improve the network.
Thanks in advance!
Here is my code:
int inCount = 2;
int hidCount = 3;
int outCount = 1;
float learningRate = 0.01;
float[] inputs = new float[inCount];
float[] hidden = new float[hidCount];
float[] outputs = new float[outCount];
float[][] IHweights = new float[inCount][hidCount];
float[][] HOweights = new float[hidCount][outCount];
void setup ()
{
for (int i = 0; i < IHweights.length; i++)
{
for (int e = 0; e < IHweights[i].length; e++)
{
float newWeight = random(-1,1);
while(newWeight > -0.5 && newWeight < 0.5)
{
newWeight = random(-1,1);
}
IHweights[i][e] = newWeight;
println(IHweights[i][e]+"\n");
}
}
for (int i = 0; i < HOweights.length; i++)
{
for (int e = 0; e < HOweights[i].length; e++)
{
float newWeight = random(-1,1);
while(newWeight > -0.5 && newWeight < 0.5)
{
newWeight = random(-1,1);
}
HOweights[i][e] = newWeight;
}
}
}
void draw ()
{
float[] inData = {round(random(1)),round(random(1))};
println(inData[0]+" "+inData[1]);
float[] expResult = {(int) inData[0]^(int) inData[1]};
println(" -> "+expResult[0]);
feedForward(inData,expResult);
}
public float sigmoid (float x)
{
if (x>10)
{
return 1;
}
if (x<-10)
{
return 0;
}
return 1/(1+exp(-x));
}
public void feedForward (float[] input, float[] expOut)
{
inputs = input;
for (int i = 0; i < hidCount; i++)
{
float var = 0;
for (int e = 0; e < inCount; e++)
{
var += inputs[e] * IHweights[e][i];
}
hidden[i] = sigmoid(var);
}
for (int i = 0; i < outCount; i++)
{
float var = 0;
for (int e = 0; e < hidCount; e++)
{
var += hidden[e] * HOweights[e][i];
}
outputs[i] = sigmoid(var);
}
float[] error = new float[outCount];
float[] deltaOut = new float[outCount];
for (int i = 0; i < outCount; i++)
{
error[i] = expOut[i] - outputs[i];
deltaOut[i] = outputs[i] * (1-outputs[i]) * error[i];
}
float[] deltaHid = new float[hidCount];
for (int i = 0; i < hidCount; i++)
{
deltaHid[i] = hidden[i] * (1-hidden[i]);
for (int e = 0; e < outCount; e++)
{
deltaHid[i] += HOweights[i][e] * outputs[e];
}
}
for (int i = 0; i < inCount; i++)
{
for (int e = 0; e < hidCount; e++)
{
IHweights[i][e] += inputs[i] * deltaHid[e] * learningRate;
}
}
for (int i = 0; i < hidCount; i++)
{
for (int e = 0; e < outCount; e++)
{
HOweights[i][e] += hidden[i] * deltaOut[e] * learningRate;
}
}
println(error[0]);
}
I am new to programming and have programmed a java program that calculates the voronoi diagram for random points and draws it on a picture - 1080 x 1920 pixels. Here you can see the result
The problem is that it takes nearly four minutes to compile the code. Please help me with code optimization. Below you can find the code:
class Voronoi {
private static Random r;
private static KDTree<Integer> kd;
private static double[][] keys;
private static int counter;
private static int x;
public static void main(String[] args) throws IOException,
KeySizeException, KeyDuplicateException {
BufferedImage img = null;
Scanner sc = new Scanner(System.in);
x = sc.nextInt();
// input an image
img = ImageIO.read(new File("input.jpg"));
// get image Height and Width
int w = img.getWidth();
int h = img.getHeight();
// make array with equal size like the image and populate it with the 0
// make an empty array and populate it
int[][] empty = new int[h][w];
for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) {
empty[row][col] = 0;
}
}
// make a D-dimensional KD-tree
keys = new double[x][2];
kd = new KDTree<Integer>(2);
ArrayList<Color> b = new ArrayList<Color>();
int[] totalBlue = new int[x];
int[] totalGreen = new int[x];
int[] totalRed = new int[x];
int[] counter_sec = new int[x];
// Generate random centers and populate them with 1
for (int i = 0; i < x; i++) {
generateCentre(empty, h, w);
// b.add(new Color(r.nextInt(256),r.nextInt(256),r.nextInt(256)));
}
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
double[] array = new double[2];
array[0] = i;
array[1] = j;
Color c = new Color(img.getRGB(j, i));
totalBlue[kd.nearest(array)] = totalBlue[kd.nearest(array)]
+ c.getBlue();
totalRed[kd.nearest(array)] = totalRed[kd.nearest(array)]
+ c.getRed();
totalGreen[kd.nearest(array)] = totalGreen[kd.nearest(array)]
+ c.getGreen();
// img.setRGB(j, i, b.get(kd.nearest(array)).getRGB());
counter_sec[kd.nearest(array)] = counter_sec[kd.nearest(array)] + 1;
}
}
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
double[] array = new double[2];
array[0] = i;
array[1] = j;
Color c = new Color(img.getRGB(j, i));
// img.setRGB(j, i, b.get(kd.nearest(array)).getRGB());
Color color = new Color(totalRed[kd.nearest(array)]/counter_sec[kd.nearest(array)], totalGreen[kd.nearest(array)]/counter_sec[kd.nearest(array)],totalBlue[kd.nearest(array)]/counter_sec[kd.nearest(array)]);
img.setRGB(j, i, color.getRGB());
}
}
File outputfile = new File("image.jpg");
ImageIO.write(img, "jpg", outputfile);
System.out.println(totalRed[0]/counter_sec[0]+" "+totalGreen[0]/counter_sec[0]+" "+totalBlue[0]/counter_sec[0]);
}
public static void generateCentre(int[][] empty, int h, int w)
throws KeySizeException, KeyDuplicateException {
r = new Random();
int height = r.nextInt(h);
int width = r.nextInt(w);
for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) {
if (row == height && col == width && empty[height][width] == 0) {
empty[height][width] = 1;
keys[counter][0] = row;
keys[counter][2] = col;
kd.insert(keys[counter], counter);
}/*else if (row == height && col == width
&& empty[height][width] != 0) {
generateCentre(empty, h, w);
}*/
}
}
System.out.println(kd.search(keys[counter]));
if (counter < x) {
counter++;
}
}
}
Although I don't claim a full understanding of your code, I see one potentially large cause of slowdown:
totalBlue[kd.nearest(array)] = totalBlue[kd.nearest(array)]
+ c.getBlue();
totalRed[kd.nearest(array)] = totalRed[kd.nearest(array)]
+ c.getRed();
totalGreen[kd.nearest(array)] = totalGreen[kd.nearest(array)]
+ c.getGreen();
// img.setRGB(j, i, b.get(kd.nearest(array)).getRGB());
counter_sec[kd.nearest(array)] = counter_sec[kd.nearest(array)] + 1;
You seem to be repeatedly calling kd.nearest(array), which is probably not a very cheap operation. You should call that once and store it in a local variable. I would rewrite your key loop as follows:
double[] coords = new double[2];
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
coords[0] = i;
coords[1] = j;
final int nearest = kd.nearest(coords);
final Color c = new Color(img.getRGB(j, i));
totalBlue[nearest] += c.getBlue();
totalRed[nearest] += c.getRed();
totalGreen[nearest] += c.getGreen();
counter_sec[nearest]++;
}
}
Here I have utilized the += and ++ operators to make things shorter and more readable. I have also hoisted your coordinates array outside the loop because it can be safely reused at each iteration. This reduces GC time, but more importantly it helps CPU caches.
Apply the same changes to your second loop, where you generate the output image.
I am trying to train a two-state Hidden Markov model with a scaled Baum-Welch, but I noticed when my emission sequence is too small. My probabilities turn to NaN in java. Is this normal? I have posted my code in java below:
import java.util.ArrayList;
/*
Scaled Baum-Welch Algorithm implementation
author: Ricky Chang
*/
public class HMModeltest {
public static double[][] stateTransitionMatrix = new double[2][2]; // State Transition Matrix
public static double[][] emissionMatrix; // Emission Probability Matrix
public static double[] pi = new double[2]; // Initial State Distribution
double[] scaler; // This is used for scaling to prevent underflow
private static int emissions_id = 1; // To identify if the emissions are for the price changes or spread changes
private static int numEmissions = 0; // The amount of emissions
private static int numStates = 2; // The number of states in hmm
public static double improvementVar; // Used to assess how much the model has improved
private static double genState; // Generated state, it is used to generate observations below
// Create an ArrayList to store the emissions
public static ArrayList<Integer> eSequence = new ArrayList<Integer>();
// Initialize H, emission_id: 1 is price change, 2 are spreads; count is for the amount of different emissions
public HMModeltest(int id, int count){
emissions_id = id;
numEmissions = count;
stateTransitionMatrix = set2DValues(numStates,numStates); // Give the STM row stochastic values
emissionMatrix = new double[numStates][numEmissions];
emissionMatrix = set2DValues(numStates,numEmissions); // Give the Emission probability matrix row stochastic values
pi = set1DValues(numStates); // Give the initial matrix row stochastic values
}
// Categorize the price change emissions; I may want to put these in the Implementation.
private int identifyE1(double e){
if( e == 0) return 4;
if( e > 0){
if(e == 1) return 5;
else if(e == 3) return 6;
else if(e == 5) return 7;
else return 8;
}
else{
if(e == -1) return 3;
else if(e == -3) return 2;
else if(e == -5) return 1;
else return 0;
}
}
// Categorize the spread emissions
private int identifyE2(double e){
if(e == 1) return 0;
else if(e == 3) return 1;
else return 2;
}
public void updateE(int emission){
if(emissions_id == 1) eSequence.add( identifyE1(emission) );
else eSequence.add( identifyE2(emission) );
}
// Used to intialize random row stochastic values to vectors
private double[] set1DValues(int col){
double sum = 0;
double temp = 0;
double [] returnVector = new double[col];
for(int i = 0; i < col; i++){
temp = Math.round(Math.random() * 1000);
returnVector[i] = temp;
sum = sum + temp;
}
for(int i = 0; i < col; i++){
returnVector[i] = returnVector[i] / sum;
}
return returnVector;
}
// Used to initialize random row stochastic values to matrices
public double[][] set2DValues(int row, int col){
double sum = 0;
double temp = 0;
double[][] returnMatrix = new double[row][col];
for(int i = 0; i < row; i++){
for(int j = 0; j < col; j++){
temp = Math.round(Math.random() * 1000);
returnMatrix[i][j] = temp;
sum = sum + temp;
}
for(int j = 0; j < col; j++){
returnMatrix[i][j] = returnMatrix[i][j] / sum;
}
sum = 0;
}
return returnMatrix;
}
// Use forward algorithm to calculate alpha for all states and times
public double[][] forwardAlgo(int time){
double alpha[][] = new double[numStates][time];
scaler = new double[time];
// Intialize alpha for time 0
scaler[0] = 0; // c0 is for scaling purposes to avoid underflow
for(int i = 0; i < numStates; i ++){
alpha[i][0] = pi[i] * emissionMatrix[i][eSequence.get(0)];
scaler[0] = scaler[0] + alpha[i][0];
}
// Scale alpha_0
scaler[0] = 1 / scaler[0];
for(int i = 0; i < numStates; i++){
alpha[i][0] = scaler[0] * alpha[i][0];
}
// Use recursive method to calculate alpha
double tempAlpha = 0;
for(int t = 1; t < time; t++){
scaler[t] = 0;
for(int i = 0; i < numStates; i++){
for(int j = 0; j < numStates; j++){
tempAlpha = tempAlpha + alpha[j][t-1] * stateTransitionMatrix[j][i];
}
alpha[i][t] = tempAlpha * emissionMatrix[i][eSequence.get(t)];
scaler[t] = scaler[t] + alpha[i][t];
tempAlpha = 0;
}
scaler[t] = 1 / scaler[t];
for(int i = 0; i < numStates; i++){
alpha[i][t] = scaler[t] * alpha[i][t];
}
}
System.out.format("scaler: ");
for(int t = 0; t < time; t++){
System.out.format("%f, ", scaler[t]);
}
System.out.print('\n');
return alpha;
}
// Use backward algorithm to calculate beta for all states
public double[][] backwardAlgo(int time){
double beta[][] = new double[2][time];
// Intialize beta for current time
for(int i = 0; i < numStates; i++){
beta[i][time-1] = scaler[time-1];
}
// Use recursive method to calculate beta
double tempBeta = 0;
for(int t = time-2; t >= 0; t--){
for(int i = 0; i < numStates; i++){
for(int j = 0; j < numStates; j++){
tempBeta = tempBeta + (stateTransitionMatrix[i][j] * emissionMatrix[j][eSequence.get(t+1)] * beta[j][t+1]);
}
beta[i][t] = tempBeta;
beta[i][t] = scaler[t] * beta[i][t];
tempBeta = 0;
}
}
return beta;
}
// Calculate the probability of emission sequence given the model (it is also the denominator to calculate gamma and digamma)
public double calcP(int t, double[][] alpha, double[][] beta){
double p = 0;
for(int i = 0; i < numStates; i++){
for(int j = 0; j < numStates; j++){
p = p + (alpha[i][t] * stateTransitionMatrix[i][j] * emissionMatrix[j][eSequence.get(t+1)] * beta[j][t+1]);
}
}
return p;
}
// Calculate digamma; i and j are both states
public double calcDigamma(double p, int t, int i, int j, double[][] alpha, double[][] beta){
double digamma = (alpha[i][t] * stateTransitionMatrix[i][j] * emissionMatrix[j][eSequence.get(t+1)] * beta[j][t+1]) / p;
return digamma;
}
public void updatePi(double[][] gamma){
for(int i = 0; i < numStates; i++){
pi[i] = gamma[i][0];
}
}
public void updateAll(){
int time = eSequence.size();
double alpha[][] = forwardAlgo(time);
double beta[][] = backwardAlgo(time);
double initialp = calcLogEProb(time);
double nextState0, nextState1;
double p = 0;
double[][][] digamma = new double[numStates][numStates][time];
double[][] gamma = new double[numStates][time];
for(int t = 0; t < time-1; t++){
p = calcP(t, alpha, beta);
for(int i = 0; i < numStates; i++){
gamma[i][t] = 0;
for(int j = 0; j < numStates; j++){
digamma[i][j][t] = calcDigamma(p, t, i, j, alpha, beta);
gamma[i][t] = gamma[i][t] + digamma[i][j][t];
}
}
}
updatePi(gamma);
updateA(digamma, gamma);
updateB(gamma);
alpha = forwardAlgo(time);
double postp = calcLogEProb(time);
improvementVar = postp - initialp;
}
// Update the state transition matrix
public void updateA(double[][][] digamma, double[][] gamma){
int time = eSequence.size();
double num = 0;
double denom = 0;
for(int i = 0; i < numStates; i++){
for(int j = 0; j < numStates; j++){
for(int t = 0; t < time-1; t++){
num = num + digamma[i][j][t];
denom = denom + gamma[i][t];
}
stateTransitionMatrix[i][j] = num/denom;
num = 0;
denom = 0;
}
}
}
public void updateB(double[][] gamma){
int time = eSequence.size();
double num = 0;
double denom = 0;
// k is an observation, j is a state, t is time
for(int i = 0; i < numStates; i++){
for(int k = 0; k < numEmissions; k++){
for(int t = 0; t < time-1; t++){
if( eSequence.get(t) == k) num = num + gamma[i][t];
denom = denom + gamma[i][t];
}
emissionMatrix[i][k] = num/denom;
num = 0;
denom = 0;
}
}
}
public double calcLogEProb(int time){
double logProb = 0;
for(int t = 0; t < time; t++){
logProb = logProb + Math.log(scaler[t]);
}
return -logProb;
}
public double calcNextState(int time, int state, double[][] gamma){
double p = 0;
for(int i = 0; i < numStates; i++){
for(int j = 0; j < numStates; j++){
p = p + gamma[i][time-2] * stateTransitionMatrix[i][j] * stateTransitionMatrix[j][state];
}
}
return p;
}
// Print parameters
public void print(){
System.out.println("Pi:");
System.out.print('[');
for(int i = 0; i < 2; i++){
System.out.format("%f, ", pi[i]);
}
System.out.print(']');
System.out.print('\n');
System.out.println("A:");
for(int i = 0; i < 2; i++){
System.out.print('[');
for(int j = 0; j < 2; j++){
System.out.format("%f, ", stateTransitionMatrix[i][j]);
}
System.out.print(']');
System.out.print('\n');
}
System.out.println("B:");
for(int i = 0; i < 2; i++){
System.out.print('[');
for(int j = 0; j < 9; j++){
System.out.format("%f, ", emissionMatrix[i][j]);
}
System.out.print(']');
System.out.print('\n');
}
System.out.print('\n');
}
/* Generate sample data to test HMM training with the following params:
* [ .3, .7 ]
* [ .8, .2 ] [ .45 .1 .08 .05 .03 .02 .05 .2 .02 ]
* [ .36 .02 .06 .15 .04 .05 .2 .1 .02 ]
* With these as observations: {-10, -5, -3, -1, 0, 1, 3, 5, 10}
*/
public static int sampleDataGen(){
double rand = 0;
rand = Math.random();
if(genState == 1){
if(rand < .3) genState = 1;
else genState = 2;
rand = Math.random();
if(rand < .45) return -10;
else if(rand < .55) return -5;
else if(rand < .63) return -3;
else if(rand < .68) return -1;
else if(rand < .71) return 0;
else if(rand < .73) return 1;
else if(rand < .78) return 3;
else if(rand < .98) return 5;
else return 10;
}
else {
if(rand < .8) genState = 1;
else genState = 2;
rand = Math.random();
if(rand < .36) return -10;
else if(rand < .38) return -5;
else if(rand < .44) return -3;
else if(rand < .59) return -1;
else if(rand < .63) return 0;
else if(rand < .68) return 1;
else if(rand < .88) return 3;
else if(rand < .98) return 5;
else return 10;
}
}
public static void main(String[] args){
HMModeltest test = new HMModeltest(1,9);
test.print();
System.out.print('\n');
for(int i = 0; i < 20; i++){
test.updateE(sampleDataGen());
}
test.updateAll();
System.out.print('\n');
test.print();
System.out.print('\n');
for(int i = 0; i < 10; i++){
test.updateE(sampleDataGen());
}
test.updateAll();
System.out.print('\n');
test.print();
System.out.print('\n');
}
}
My guess is that since the sample is too small, sometimes the probabilities don't exist for some observations. But it would be nice to have some confirmation.
You could refer the "Scaling" section in Rabiner's paper, which solves the underflow problem.
You could also do the calculations in log space, that's what HTK and R do. Multiplication and division become addition and subtraction. For the other two, look at the LAdd/ LSub and logspace_add/logspace_sub functions in the respective toolkits.
The log-sum-exp trick might be helpful too.
My head is not working anymore. I have to finish this a.s.a.p
How can i make this for loop shorter.
At the moment i have got 4 different for loop. I want to combine them and have only one.
Card[] cards = new Card[4*13];
void testCreateCards() {
int k = 0;
for (int suit = 0; suit <= 3; suit++) { // for suit
for (int value = 1; value <= 13; value++) { // from Ace to King
// build new card
cards[k++] = new Card(suit, value);
}
}
}
void testDrawClubs() {
int x = 0;
int y = 0;
for (int i = 0; i <= 12; i++) {
cards[i].displayCard(x, y);
x +=80;
}
}
void testDrawDiamonds() {
int x = 0;
int y = 80;
for (int i = 13; i <= 25; i++) {
cards[i].displayCard(x, y);
x +=80;
}
}
void testDrawHearts() {
int x = 0;
int y = 160;
for (int i = 26; i <= 38; i++) {
cards[i].displayCard(x, y);
x +=80;
}
}
void testDrawSpades() {
int x = 0;
int y = 240;
for (int i = 39; i <= 51; i++) {
cards[i].displayCard(x, y);
x +=80;
}
}
You can calculate x and y directly using modular arithmetic because i % 13 gives you the x-coordinate and i / 13 gives you the y-coordinate:
void testDrawCards() {
for (int i = 0; i < 52; i++) {
int x = i % 13;
int y = i / 13;
cards[i].displayCard(x * 80, y * 80);
}
}