C#(.net10 x86) 圖片進行Otsu二值化 範例 [cs_image_OtsuThreshold]
C#(.net10 x86) 圖片進行Otsu二值化 範例 [cs_image_OtsuThreshold]
資料來源: AI(C# code)
https://zh.wikipedia.org/zh-tw/Hello_Kitty [測試素材來源]
GITHUB:https://github.com/jash-git/cs_image_OtsuThreshold
=============
方案收集
01.使用SixLabors.ImageSharp 從頭實現演算法
02.使用Emgu.CV 直接用內建的函數
03.使用OpenCvSharp4 直接用內建的函數 [不支援X32]
=============
授權方式
01. Apache License
02. GPL3/$
03. Apache License
=============
測試和結果圖
(jpg)
(去背png)
(二值化jpg)
=============
Code
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.Diagnostics;
public class ConsumeTime
{
private static Stopwatch m_stopWatch = new Stopwatch();
private static String m_StrTitle = "";
private static String m_StrStartFileLine = "";
private static String m_StrEndFileLine = "";
public static void Start(String StrInfor)
{
StackFrame CallStack = new StackFrame(1, true);
m_StrStartFileLine = String.Format("File : {0} , Line : {1}", CallStack.GetFileName(), CallStack.GetFileLineNumber());
m_StrTitle = StrInfor;
m_stopWatch.Start();
}
public static void Stop()
{
StackFrame CallStack = new StackFrame(1, true);
m_stopWatch.Stop();
// Get the elapsed time as a TimeSpan value.
TimeSpan ts = m_stopWatch.Elapsed;
// Format and display the TimeSpan value.
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
m_StrEndFileLine = String.Format("File : {0} , Line : {1}", CallStack.GetFileName(), CallStack.GetFileLineNumber());
Console.WriteLine(m_StrStartFileLine + " ~ " + m_StrEndFileLine + " consume time: " + elapsedTime, m_StrTitle);
}
}
class Program
{
static void Pause()
{
Console.Write("Press any key to continue...");
Console.ReadKey(true);
}
static void Main()
{
ConsumeTime.Start("start");
string inputPath = @"C:\Users\jashv\OneDrive\桌面\1772587654036.jpg";
string outputPath = @"C:\Users\jashv\OneDrive\桌面\output.jpg";
using var image = Image.Load<Rgba32>(inputPath);
int width = image.Width;
int height = image.Height;
int[] histogram = new int[256];
byte[,] grayData = new byte[width, height];
// 讀取像素
image.ProcessPixelRows(accessor =>
{
for (int y = 0; y < height; y++)
{
var row = accessor.GetRowSpan(y);
for (int x = 0; x < width; x++)
{
var pixel = row[x];
byte gray = (byte)(0.299 * pixel.R +
0.587 * pixel.G +
0.114 * pixel.B);
grayData[x, y] = gray;
histogram[gray]++;
}
}
});
// === Otsu 計算 ===
int totalPixels = width * height;
float sum = 0;
for (int i = 0; i < 256; i++)
sum += i * histogram[i];
float sumB = 0;
int wB = 0;
int threshold = 0;
float maxVariance = 0;
for (int t = 0; t < 256; t++)
{
wB += histogram[t];
if (wB == 0) continue;
int wF = totalPixels - wB;
if (wF == 0) break;
sumB += t * histogram[t];
float mB = sumB / wB;
float mF = (sum - sumB) / wF;
float between = wB * wF * (mB - mF) * (mB - mF);
if (between > maxVariance)
{
maxVariance = between;
threshold = t;
}
}
Console.WriteLine($"Otsu Threshold = {threshold}");
// 建立輸出圖
using var output = new Image<L8>(width, height);
output.ProcessPixelRows(accessor =>
{
for (int y = 0; y < height; y++)
{
var row = accessor.GetRowSpan(y);
for (int x = 0; x < width; x++)
{
row[x] = grayData[x, y] > threshold
? new L8(255)
: new L8(0);
}
}
});
output.Save(outputPath);
ConsumeTime.Stop();
Console.WriteLine("Done");
Pause();
}
}