teaching machines

CS 330 Lecture 21 – Languages Working Together

March 20, 2015 by . Filed under cs330, lectures, spring 2015.

Agenda

TODO

Code

Main.java

public class Main {
  public static void main(String[] args) {
    Logger.log("brad mittens");
  }
}

Logger.java

public class Logger {
  static {
    System.loadLibrary("logger");
  }

  public static native void log(String message);
}

native.cpp

#include <asl.h>

#include "native.h"

JNIEXPORT void JNICALL Java_Logger_log(JNIEnv *env, jclass, jstring jstr) {
  const char *cstr = env->GetStringUTFChars(jstr, NULL);
  asl_log_message(ASL_LEVEL_NOTICE, cstr);
  env->ReleaseStringUTFChars(jstr, cstr);
}

makefile

Note to copy and pasters: makefile rules need to be indented with real tabs, not spaces.

Main.class: Main.java Logger.java
  javac Main.java

native.h: Main.class
  javah -o $@ -force Logger

liblogger.dylib: native.cpp native.h
  g++ -shared -I /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/include -o liblogger.dylib native.cpp

clean:
  rm -rf *.dylib *.class native.h

Main.java

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class Main {
  public static void main(String[] args) throws IOException {
    JFrame frame = new JFrame("Thresh");

    final BufferedImage leftImage = ImageIO.read(new File(args[0]));
    JLabel leftLabel = new JLabel(new ImageIcon(leftImage));
    frame.add(leftLabel, BorderLayout.WEST);

    final BufferedImage rightImage = new BufferedImage(leftImage.getWidth(), leftImage.getHeight(), leftImage.getType());
    final JLabel rightLabel = new JLabel(new ImageIcon(rightImage));
    frame.add(rightLabel, BorderLayout.EAST);

    JButton processButton = new JButton("Process");
    frame.add(processButton, BorderLayout.NORTH);

    processButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        int[] pixels = leftImage.getRGB(0, 0, leftImage.getWidth(), leftImage.getHeight(), null, 0, leftImage.getWidth());
        Imogene.process(pixels, leftImage.getWidth(), leftImage.getHeight());
        rightImage.setRGB(0, 0, rightImage.getWidth(), rightImage.getHeight(), pixels, 0, rightImage.getWidth());
        rightLabel.setIcon(new ImageIcon(rightImage));
      }
    });

    frame.pack();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
  }
}

Imogene.java

public final class Imogene {
  static {
    System.loadLibrary("imogene");
  }

  public native static void process(int[] pixels, int width, int height);
}

native.cpp

#include <jni.h>
#include <iostream>

#include "native.h"

JNIEXPORT void JNICALL Java_Imogene_process(JNIEnv *env, jclass, jintArray pixels, jint width, jint height) {
  int *raster = env->GetIntArrayElements(pixels, NULL);
  int *thresholded = new int[width * height];

  for (int r = 0; r < height; ++r) {
    for (int c = 0; c < width; ++c) {
      int argb = raster[c + r * width];
      int red = (argb >> 16) & 0xff;
      int green = (argb >> 8) & 0xff;
      int blue = (argb >> 0) & 0xff;
      int gray = (int) (0.2989 * red + 0.5870 * green + 0.1140 * blue);
      
      int intensity = gray < 254 ? 1 : 0;
      /* if (gray < 254) { */
        /* raster[c + r * width] = 0xFFFFFF; */
      /* } else { */
        /* raster[c + r * width] = 0; */
      /* } */

      thresholded[c + r * width] = intensity;
    }
  }

  // Erode
  int *dilated = new int[width * height];
  for (int r = 1; r < height - 1; ++r) {
    for (int c = 1; c < width - 1; ++c) {
      if (thresholded[(c - 1) + (r - 1) * width] &&
          thresholded[(c - 1) + (r - 0) * width] &&
          thresholded[(c - 1) + (r + 1) * width] &&
          thresholded[(c - 0) + (r - 1) * width] &&
          thresholded[(c - 0) + (r - 0) * width] &&
          thresholded[(c - 0) + (r + 1) * width] &&
          thresholded[(c + 1) + (r - 1) * width] &&
          thresholded[(c + 1) + (r - 0) * width] &&
          thresholded[(c + 1) + (r + 1) * width]) {
        dilated[c + r * width] = 1;
      } else {
        dilated[c + r * width] = 0;
      }
    }
  }

  // Dilate
  for (int r = 1; r < height - 1; ++r) {
    for (int c = 1; c < width - 1; ++c) {
      if (dilated[(c - 1) + (r - 1) * width] ||
          dilated[(c - 1) + (r - 0) * width] ||
          dilated[(c - 1) + (r + 1) * width] ||
          dilated[(c - 0) + (r - 1) * width] ||
          dilated[(c - 0) + (r - 0) * width] ||
          dilated[(c - 0) + (r + 1) * width] ||
          dilated[(c + 1) + (r - 1) * width] ||
          dilated[(c + 1) + (r - 0) * width] ||
          dilated[(c + 1) + (r + 1) * width]) {
        raster[c + r * width] = 0xffffff;
      } else {
        raster[c + r * width] = 0;
      }
    }
  }

  delete[] thresholded;
  delete[] dilated;
  env->ReleaseIntArrayElements(pixels, raster, 0);
}

native.cpp.ours

#include "native.h"

JNIEXPORT void JNICALL Java_Imogene_process(JNIEnv *env, jclass, jintArray jpixels, jint width, jint height) {
  int *raster = env->GetIntArrayElements(jpixels, NULL);

  for (int r = 0; r < height; ++r) {
    for (int c = 0; c < width; ++c) {
      int argb = raster[r * width + c];
      int red = (argb >> 16) & 0xFF;
      int green = (argb >> 8) & 0xFF;
      int blue = (argb >> 0) & 0xFF;
      int grayscale = 0.2989 * red + 0.587 * green + 0.114 * blue;
      int bw = grayscale > 128 ? 255 : 0;
      raster[r * width + c] = bw | (bw << 8) | (bw << 16);
    }
  }

  env->ReleaseIntArrayElements(jpixels, raster, 0);
}

makefile

Note to copy and pasters: makefile rules need to be indented with real tabs, not spaces.

.PHONY: run

Main.class: Main.java Imogene.java
  @echo Newer than Main.class: $?
  javac Main.java

native.h: Main.class
  @echo Newer than native.h: $?
  javah -o $@ -force Imogene

libimogene.dylib: native.cpp native.h
  @echo Newer than so: $?
  g++ -shared -I/Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/include -o libimogene.dylib native.cpp

run: Main.class libimogene.dylib
  java -cp . -Djava.library.path=. Main ~/Desktop/coins5.gif

clean:
  rm -rf *.class native.h libimogene.dylib

Haiku

on stratification
The bottom is dark
Because the top gets the light
And it will stay there