Tepi (edge) adalah perubahan nilai intensitas derajat keabuan yang cepat atau tiba-tiba (besar) dalam jarak yang singkat. Tujuan mendeteksi tepi sendiri adalah untuk mengelompokkan objek-objek dalam citra, dan juga digunakan untuk menganalisis citra lebih lanjut. Ada banyak algoritma yang digunakan untuk mendeteksi tepi (sobel, prewitt, robert cross, canny, dll), salah satu diantaranya adalah deteksi tepi Canny (Canny Edge detection).
Pada pembuatan edge detection kali ini menggunakan formula canny untuk membuatan pendeteksian tepi.
Canny edge detector dikembangkan oleh John F. Canny pada tahun 1986 dan menggunakan algoritma multi-tahap untuk mendeteksi berbagai tepi dalam gambar. Yang paling penting, Canny juga menghasilkan teori komputasi deteksi tepi menjelaskan mengapa teknik ini bekerja. (wikipedia)
Pada program Canny Edge Detection ini menggunakan bahasa java untuk pembuatannya.
Penjelasan dari potongan coding dari program Canny Edge Detection menggunakan java.
1. Penjelasan codingan dari file dengan nama Canny.java
public class Canny {
public static void main(String[] args) throws IOException {
File file= new File("foto.jpg");
Codingan untuk memanggil file foto yang akan dilakukan pendeteksian tepi.
ImageIO.write(edges,"JPG",new File("foto 2.jpg"));
Codingan di atas adalah untuk membuat hasil dari foto yang telah di buat deteksi tepinya akan di save dengan nama yang telah di tentukan, yaitu foto 2.jpg nama filenya.
Catatan: bila anda ingin mengganti file foto yang akan di deteksi tepinya maka anda cukup mengganti nama file foto tersebut beserta ekstention filenya, bisa berupa *.bmp, *.jpg, *.png, *.jpeg, dll. Ganti pada bagian foto.jpg yang telah di beri warna merah.
Bila anda ingin file foto yang telah di deteksi tepi dapat di save dengan nama yang anda inginkan, dapat anda ubah di bagian foto 2.jpg yang telah di beri warna merah, sesuai dengan yang anda inginkan. Dapat di ubah juga ekstention dari file fotonya.
2. Penjelasan codingan dari file dengan nama CannyEdgeDetection.java
public class CannyEdgeDetector {
// statics
private final static float GAUSSIAN_CUT_OFF = 0.005f;
private final static float MAGNITUDE_SCALE = 100F;
private final static float MAGNITUDE_LIMIT = 1000F;
private final static int MAGNITUDE_MAX = (int) (MAGNITUDE_SCALE * MAGNITUDE_LIMIT);
// fields
private int height;
private int width;
private int picsize;
private int[] data;
private int[] magnitude;
private BufferedImage sourceImage;
private BufferedImage edgesImage;
private float gaussianKernelRadius;
private float lowThreshold;
private float highThreshold;
private int gaussianKernelWidth;
private boolean contrastNormalized;
private float[] xConv;
private float[] yConv;
private float[] xGradient;
private float[] yGradient;
Coding di atas merupakan pemberian nilai variable yang akan digunakan pada program ini.
public CannyEdgeDetector() {
lowThreshold = 2.5f;
highThreshold = 7.5f;
gaussianKernelRadius = 2f;
gaussianKernelWidth = 16;
contrastNormalized = false;
}
Pembuatan Constructor Canny Edge Detection dengan memberikan nilai pada lowThreshold 2.5f dan highThreshold 7.5f.
public BufferedImage getSourceImage() {
return sourceImage;
}
Mengambil data pencahayaan yang digunakan oleh canny untuk menghasilkan tepi.
public void setSourceImage(BufferedImage image) {
sourceImage = image;
}
Menentukan Gambar yang akan di ambil tepi dari data pencahayaan akan terdeteksi. file gambar telah di tentukan sebelum melakukan proses.
public BufferedImage getEdgesImage() {
return edgesImage;
}
Memperoleh gambar yang berisi deteksi tepi. Buffer gambar adalah sebuah gambar buram dari jenis BufferedImage.TYPE_INT_ARGB di mana piksel edge putih dan semua lainnya Piksel hitam.
public int getGaussianKernelWidth() {
return gaussianKernelWidth;
}
Jumlah piksel di mana kernel Gaussian diterapkan. Nilai default adalah 16.
public void process() {
width = sourceImage.getWidth();
height = sourceImage.getHeight();
picsize = width * height;
initArrays();
readLuminance();
if (contrastNormalized) normalizeContrast();
computeGradients(gaussianKernelRadius, gaussianKernelWidth);
int low = Math.round(lowThreshold * MAGNITUDE_SCALE);
int high = Math.round( highThreshold * MAGNITUDE_SCALE);
performHysteresis(low, high);
thresholdEdges();
writeEdges(data);
}
Untuk mengambil tinggi dan lebar dari sumber gambar. Lalu untuk menentukan picsize dengan mengkalikan ukuran lebar dan tinggi gambar serta akan diberi nilai batas bawah dan batas tingginya.
private void initArrays() {
if (data == null || picsize != data.length) {
data = new int[picsize];
magnitude = new int[picsize];
xConv = new float[picsize];
yConv = new float[picsize];
xGradient = new float[picsize];
yGradient = new float[picsize];
}
}
Untuk memberikan nilai yang di perlukan di deteksi tepi dari picsize hasil kali lebar dan tinggi gambar sumber.
private void computeGradients(float kernelRadius, int kernelWidth) {
float kernel[] = new float[kernelWidth];
float diffKernel[] = new float[kernelWidth];
int kwidth;
for (kwidth = 0; kwidth < kernelWidth; kwidth++) {
float g1 = gaussian(kwidth, kernelRadius);
if (g1 <= GAUSSIAN_CUT_OFF && kwidth >= 2) break;
float g2 = gaussian(kwidth - 0.5f, kernelRadius);
float g3 = gaussian(kwidth + 0.5f, kernelRadius);
kernel[kwidth] = (g1 + g2 + g3) / 3f / (2f * (float) Math.PI * kernelRadius * kernelRadius);
diffKernel[kwidth] = g3 - g2;
}
int initX = kwidth - 1;
int maxX = width - (kwidth - 1);
int initY = width * (kwidth - 1);
int maxY = width * (height - (kwidth - 1));
Hasil dari metode Gaussian yang digunakan.
for (int x = initX; x < maxX; x++) {
for (int y = initY; y < maxY; y += width) {
int index = x + y;
float sumX = data[index] * kernel[0];
float sumY = sumX;
int xOffset = 1;
int yOffset = width;
for(; xOffset < kwidth ;) {
sumY += kernel[xOffset] * (data[index - yOffset] + data[index + yOffset]);
sumX += kernel[xOffset] * (data[index - xOffset] + data[index + xOffset]);
yOffset += width;
xOffset++;
}
yConv[index] = sumY;
xConv[index] = sumX;
}
}
for (int x = initX; x < maxX; x++) {
for (int y = initY; y < maxY; y += width) {
float sum = 0f;
int index = x + y;
for (int i = 1; i < kwidth; i++)
sum += diffKernel[i] * (yConv[index - i] - yConv[index + i]);
xGradient[index] = sum;
}
}
for (int x = kwidth; x < width - kwidth; x++) {
for (int y = initY; y < maxY; y += width) {
float sum = 0.0f;
int index = x + y;
int yOffset = width;
for (int i = 1; i < kwidth; i++) {
sum += diffKernel[i] * (xConv[index - yOffset] - xConv[index + yOffset]);
yOffset += width;
}
yGradient[index] = sum;
}
}
initX = kwidth;
maxX = width - kwidth;
initY = width * kwidth;
maxY = width * (height - kwidth);
for (int x = initX; x < maxX; x++) {
for (int y = initY; y < maxY; y += width) {
int index = x + y;
int indexN = index - width;
int indexS = index + width;
int indexW = index - 1;
int indexE = index + 1;
int indexNW = indexN - 1;
int indexNE = indexN + 1;
int indexSW = indexS - 1;
int indexSE = indexS + 1;
float xGrad = xGradient[index];
float yGrad = yGradient[index];
float gradMag = hypot(xGrad, yGrad);
//perform non-maximal supression
float nMag = hypot(xGradient[indexN], yGradient[indexN]);
float sMag = hypot(xGradient[indexS], yGradient[indexS]);
float wMag = hypot(xGradient[indexW], yGradient[indexW]);
float eMag = hypot(xGradient[indexE], yGradient[indexE]);
float neMag = hypot(xGradient[indexNE], yGradient[indexNE]);
float seMag = hypot(xGradient[indexSE], yGradient[indexSE]);
float swMag = hypot(xGradient[indexSW], yGradient[indexSW]);
float nwMag = hypot(xGradient[indexNW], yGradient[indexNW]);
float tmp;
Melakukan perhitungan untuk x dan y. Maka nantinya gambar tersebut yang akan di deteksi tepi dan mengalami perubahan.
private void thresholdEdges() {
for (int i = 0; i < picsize; i++) {
data[i] = data[i] > 0 ? -1 : 0xff000000;
}
}
Menentukan dari nilai threshold tepinya. Bila lebih dari batas atas maka warna hitam bila kurang maka berwarna putih.
private int luminance(float r, float g, float b) {
return Math.round(0.299f * r + 0.587f * g + 0.114f * b);
}
Perhitungan nilai RGB dari luminance.
private void readLuminance() {
int type = sourceImage.getType();
if (type == BufferedImage.TYPE_INT_RGB || type == BufferedImage.TYPE_INT_ARGB) {
int[] pixels = (int[]) sourceImage.getData().getDataElements(0, 0, width, height, null);
for (int i = 0; i < picsize; i++) {
int p = pixels[i];
int r = (p & 0xff0000) >> 16;
int g = (p & 0xff00) >> 8;
int b = p & 0xff;
data[i] = luminance(r, g, b);
}
} else if (type == BufferedImage.TYPE_BYTE_GRAY) {
byte[] pixels = (byte[]) sourceImage.getData().getDataElements(0, 0, width, height, null);
for (int i = 0; i < picsize; i++) {
data[i] = (pixels[i] & 0xff);
}
} else if (type == BufferedImage.TYPE_USHORT_GRAY) {
short[] pixels = (short[]) sourceImage.getData().getDataElements(0, 0, width, height, null);
for (int i = 0; i < picsize; i++) {
data[i] = (pixels[i] & 0xffff) / 256;
}
} else if (type == BufferedImage.TYPE_3BYTE_BGR) {
byte[] pixels = (byte[]) sourceImage.getData().getDataElements(0, 0, width, height, null);
int offset = 0;
for (int i = 0; i < picsize; i++) {
int b = pixels[offset++] & 0xff;
int g = pixels[offset++] & 0xff;
int r = pixels[offset++] & 0xff;
data[i] = luminance(r, g, b);
}
} else {
throw new IllegalArgumentException("Unsupported image type: " + type);
}
}
Proses perhitungan Luminance. Menentukan warna RGB untuk gambar.
private void normalizeContrast() {
int[] histogram = new int[256];
for (int i = 0; i < data.length; i++) {
histogram[data[i]]++;
}
int[] remap = new int[256];
int sum = 0;
int j = 0;
for (int i = 0; i < histogram.length; i++) {
sum += histogram[i];
int target = sum*255/picsize;
for (int k = j+1; k <=target; k++) {
remap[k] = i;
}
j = target;
}
for (int i = 0; i < data.length; i++) {
data[i] = remap[data[i]];
}
}
Melakukan normalisai contrast dari gambar dengan cara histogram.
Berikut adalah penjelasan dari potongan coding untuk program pendeteksian tepi menggunakan metode Canny dengan menggunakan bahasa pemograman Java.
Berikut adalah hasil gambar dari proses pendeteksian tepi menggunakan Canny pada program java ini;
Hasil Gambar Canny Edge Detection |
Dari hasil pendeteksian tepi menggunakan Canny pada program kami ini, dapat terlihat perbedaan yang mencolok. Sehingga pada program yang kami buat lebih baik bila hasil dari gambar sumber untuk pendeteksian tepinya dengan file bertipe *.png.
Serta kelemahan dari program kami adalah tidak terdapatnya GUI sehingga bila ingin mengganti gambar harus mengganti secara langsung dalam codingan.
Untuk menjalankan program ini dengan menggunakan terminal (CMD) pada computer.
Nama Kelompok :
Mufi Widyanti (50408553)
Rivan Perdana Putra (50408733)
Kelas :
4IA11
Universitas Gunadarma
0 komentar:
Posting Komentar