Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
mcychan authored Sep 23, 2024
1 parent 83b47c0 commit edfd36d
Showing 1 changed file with 158 additions and 13 deletions.
171 changes: 158 additions & 13 deletions nQuant.Master/Otsu.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/* Otsu's Image Segmentation Method
Copyright (C) 2009 Tolga Birdal
Copyright (c) 2018-2021 Miller Cy Chan
Copyright (c) 2018-2024 Miller Cy Chan
*/

using nQuant.Master;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;

namespace OtsuThreshold
{
Expand Down Expand Up @@ -94,7 +95,7 @@ private short GetOtsuThreshold(int[] pixels)
return FindMax(vet, 256);
}

private void Threshold(int[] pixels, short thresh, float weight = 1f)
private void Threshold(int[] pixels, int[] dest, short thresh, float weight = 1f)
{
var maxThresh = (byte)thresh;
if (thresh >= 200)
Expand All @@ -104,17 +105,158 @@ private void Threshold(int[] pixels, short thresh, float weight = 1f)
thresh = 200;
}

var minThresh = (byte)(thresh * weight);
var minThresh = (byte)(thresh * (m_transparentPixelIndex >= 0 ? .9f : weight));
var shadow = m_transparentPixelIndex >= 0 ? 3.5 : 3;
for (int i = 0; i < pixels.Length; ++i)
{
var c = Color.FromArgb(pixels[i]);
if (c.R + c.G + c.B > maxThresh * 3)
pixels[i] = Color.FromArgb(c.A, Byte.MaxValue, Byte.MaxValue, Byte.MaxValue).ToArgb();
else if (m_transparentPixelIndex >= 0 || c.R + c.G + c.B < minThresh * 3)
pixels[i] = Color.FromArgb(c.A, 0, 0, 0).ToArgb();
if (c.A < alphaThreshold && c.R + c.G + c.B > maxThresh * 3)
dest[i] = Color.FromArgb(c.A, Byte.MaxValue, Byte.MaxValue, Byte.MaxValue).ToArgb();
else if (c.R + c.G + c.B < minThresh * shadow)
dest[i] = Color.FromArgb(c.A, 0, 0, 0).ToArgb();
}
}

private int[] CannyFilter(int width, int[] pixelsGray, double lowerThreshold, double higherThreshold) {
int height = pixelsGray.Length / width;
int area = width * height;

var pixelsCanny = Enumerable.Repeat(Color.White.ToArgb(), area).ToArray();

var gx = new int[3, 3]{{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}};
var gy = new int[3, 3]{{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}};
var G = new double[area];
var theta = new int[area];
var largestG = 0.0;

// perform canny edge detection on everything but the edges
for (int i = 1; i < height - 1; ++i) {
for (int j = 1; j < width - 1; ++j) {
// find gx and gy for each pixel
var gxValue = 0.0;
var gyValue = 0.0;
for (int x = -1; x <= 1; ++x) {
for (int y = -1; y <= 1; ++y) {
var c = Color.FromArgb(pixelsGray[(i + x) * width + j + y]);
gxValue += gx[1 - x, 1 - y] * c.G;
gyValue += gy[1 - x, 1 - y] * c.G;
}
}

int center = i * width + j;
// calculate G and theta
G[center] = Math.Sqrt(Math.Pow(gxValue, 2) + Math.Pow(gyValue, 2));
var atanResult = Math.Atan2(gyValue, gxValue) * 180.0 / Math.PI;
theta[center] = (int)(180.0 + atanResult);

if (G[center] > largestG)
largestG = G[center];

// setting the edges
if (i == 1) {
G[center - 1] = G[center];
theta[center - 1] = theta[center];
}
else if (j == 1) {
G[center - width] = G[center];
theta[center - width] = theta[center];
}
else if (i == height - 1) {
G[center + 1] = G[center];
theta[center + 1] = theta[center];
}
else if (j == width - 1) {
G[center + width] = G[center];
theta[center + width] = theta[center];
}

// setting the corners
if (i == 1 && j == 1) {
G[center - width - 1] = G[center];
theta[center - width - 1] = theta[center];
}
else if (i == 1 && j == width - 1) {
G[center - width + 1] = G[center];
theta[center - width + 1] = theta[center];
}
else if (i == height - 1 && j == 1) {
G[center + width - 1] = G[center];
theta[center + width - 1] = theta[center];
}
else if (i == height - 1 && j == width - 1) {
G[center + width + 1] = G[center];
theta[center + width + 1] = theta[center];
}

// to the nearest 45 degrees
theta[center] = (int) Math.Round(theta[center] / 45.0) * 45;
}
}

largestG *= .5;

// non-maximum suppression
for (int i = 1; i < height - 1; ++i) {
for (int j = 1; j < width - 1; ++j) {
int center = i * width + j;
if (theta[center] == 0 || theta[center] == 180) {
if (G[center] < G[center - 1] || G[center] < G[center + 1])
G[center] = 0;
}
else if (theta[center] == 45 || theta[center] == 225) {
if (G[center] < G[center + width + 1] || G[center] < G[center - width - 1])
G[center] = 0;
}
else if (theta[center] == 90 || theta[center] == 270) {
if (G[center] < G[center + width] || G[center] < G[center - width])
G[center] = 0;
}
else {
if (G[center] < G[center + width - 1] || G[center] < G[center - width + 1])
G[center] = 0;
}

var grey = Byte.MaxValue - (byte)(G[center] * (255.0 / largestG));
var c = Color.FromArgb(pixelsGray[center]);
pixelsCanny[center] = Color.FromArgb(c.A, grey, grey, grey).ToArgb();
}
}

int k = 0;
var minThreshold = lowerThreshold * largestG;
var maxThreshold = higherThreshold * largestG;
do {
for (int i = 1; i < height - 1; ++i) {
for (int j = 1; j < width - 1; ++j) {
int center = i * width + j;
if (G[center] < minThreshold)
G[center] = 0;
else if (G[center] >= maxThreshold)
continue;
else if (G[center] < maxThreshold) {
G[center] = 0;
for (int x = -1; x <= 1; ++x) {
for (int y = -1; y <= 1; y++) {
if (x == 0 && y == 0)
continue;
if (G[center + x * width + y] >= maxThreshold) {
G[center] = higherThreshold * largestG;
k = 0;
x = 2;
break;
}
}
}
}

var grey = Byte.MaxValue - (byte)(G[center] * 255.0 / largestG);
var c = Color.FromArgb(pixelsGray[center]);
pixelsCanny[center] = Color.FromArgb(c.A, grey, grey, grey).ToArgb();
}
}
} while (k++ < 100);
return pixelsCanny;
}

public ushort DitherColorIndex(Color[] palette, int pixel, int pos)
{
Expand Down Expand Up @@ -216,7 +358,7 @@ public Bitmap ConvertToGrayScale(Bitmap srcimg)
return sourceImg;
}

private void ConvertToGrayScale(int[] pixels)
private void ConvertToGrayScale(int[] pixels, int[] dest)
{
float min1 = Byte.MaxValue;
float max1 = .0f;
Expand All @@ -243,7 +385,7 @@ private void ConvertToGrayScale(int[] pixels)

int green = (pixels[i] >> 8) & 0xff;
var grey = (int)((green - min1) * (Byte.MaxValue / (max1 - min1)));
pixels[i] = Color.FromArgb(alfa, grey, grey, grey).ToArgb();
dest[i] = Color.FromArgb(alfa, grey, grey, grey).ToArgb();
}
}

Expand All @@ -259,11 +401,14 @@ public Bitmap ConvertGrayScaleToBinary(Bitmap srcimg, bool isGrayscale = false)
return srcimg;
hasSemiTransparency = semiTransCount > 0;

var pixelsGray = (int[]) pixels.Clone();
if (!isGrayscale)
ConvertToGrayScale(pixels);
ConvertToGrayScale(pixels, pixelsGray);

var otsuThreshold = GetOtsuThreshold(pixels);
Threshold(pixels, otsuThreshold);
var otsuThreshold = GetOtsuThreshold(pixelsGray);
double lowerThreshold = 0.03, higherThreshold = 0.1;
pixels = CannyFilter(bitmapWidth, pixelsGray, lowerThreshold, higherThreshold);
Threshold(pixelsGray, pixels, otsuThreshold);

var dest = new Bitmap(bitmapWidth, bitmapHeight, PixelFormat.Format1bppIndexed);
var palettes = dest.Palette.Entries;
Expand All @@ -290,5 +435,5 @@ public Bitmap ConvertGrayScaleToBinary(Bitmap srcimg, bool isGrayscale = false)
return BitmapUtilities.ProcessImagePixels(dest, palettes, qPixels, m_transparentPixelIndex >= 0);
}

}
}
}

0 comments on commit edfd36d

Please sign in to comment.