优化:修复Logger类型错误,重写CLAHE算法,像素遍历改用unsafe指针加速
This commit is contained in:
@@ -135,38 +135,94 @@ public class ContrastProcessor : ImageProcessorBase
|
||||
int tileSize = 8;
|
||||
int width = inputImage.Width;
|
||||
int height = inputImage.Height;
|
||||
byte[,,] srcData = inputImage.Data;
|
||||
|
||||
// 计算分块数
|
||||
int tilesX = (width + tileSize - 1) / tileSize;
|
||||
int tilesY = (height + tileSize - 1) / tileSize;
|
||||
int actualTileW = (width + tilesX - 1) / tilesX;
|
||||
int actualTileH = (height + tilesY - 1) / tilesY;
|
||||
|
||||
var result = new Image<Gray, byte>(width, height);
|
||||
|
||||
// 为每个 tile 计算带 clip limit 的均衡化映射表
|
||||
var luts = new byte[tilesY, tilesX, 256];
|
||||
for (int ty = 0; ty < tilesY; ty++)
|
||||
{
|
||||
for (int tx = 0; tx < tilesX; tx++)
|
||||
{
|
||||
int x = tx * tileSize;
|
||||
int y = ty * tileSize;
|
||||
int w = Math.Min(tileSize, width - x);
|
||||
int h = Math.Min(tileSize, height - y);
|
||||
int x0 = tx * actualTileW;
|
||||
int y0 = ty * actualTileH;
|
||||
int x1 = Math.Min(x0 + actualTileW, width);
|
||||
int y1 = Math.Min(y0 + actualTileH, height);
|
||||
int tilePixels = (x1 - x0) * (y1 - y0);
|
||||
|
||||
var roi = new System.Drawing.Rectangle(x, y, w, h);
|
||||
inputImage.ROI = roi;
|
||||
var tile = inputImage.Copy();
|
||||
inputImage.ROI = System.Drawing.Rectangle.Empty;
|
||||
// 构建直方图
|
||||
var hist = new int[256];
|
||||
for (int y = y0; y < y1; y++)
|
||||
for (int x = x0; x < x1; x++)
|
||||
hist[srcData[y, x, 0]]++;
|
||||
|
||||
var equalizedTile = new Image<Gray, byte>(tile.Size);
|
||||
CvInvoke.EqualizeHist(tile, equalizedTile);
|
||||
// Clip limit 裁剪并重新分配
|
||||
int clipThreshold = (int)(clipLimit * tilePixels / 256);
|
||||
if (clipThreshold > 0)
|
||||
{
|
||||
int excess = 0;
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
if (hist[i] > clipThreshold)
|
||||
{
|
||||
excess += hist[i] - clipThreshold;
|
||||
hist[i] = clipThreshold;
|
||||
}
|
||||
}
|
||||
int avgInc = excess / 256;
|
||||
int remainder = excess - avgInc * 256;
|
||||
for (int i = 0; i < 256; i++)
|
||||
hist[i] += avgInc + (i < remainder ? 1 : 0);
|
||||
}
|
||||
|
||||
result.ROI = roi;
|
||||
equalizedTile.CopyTo(result);
|
||||
result.ROI = System.Drawing.Rectangle.Empty;
|
||||
|
||||
tile.Dispose();
|
||||
equalizedTile.Dispose();
|
||||
// 构建 CDF 映射表
|
||||
int sum = 0;
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
sum += hist[i];
|
||||
luts[ty, tx, i] = (byte)Math.Clamp(sum * 255 / tilePixels, 0, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
_logger.Debug("ApplyCLAHE");
|
||||
|
||||
// 双线性插值生成结果
|
||||
var result = new Image<Gray, byte>(width, height);
|
||||
byte[,,] dstData = result.Data;
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
// 计算当前像素所在 tile 的中心坐标
|
||||
double fx = (double)x / actualTileW - 0.5;
|
||||
double fy = (double)y / actualTileH - 0.5;
|
||||
int tx0 = Math.Max(0, (int)fx);
|
||||
int ty0 = Math.Max(0, (int)fy);
|
||||
int tx1 = Math.Min(tx0 + 1, tilesX - 1);
|
||||
int ty1 = Math.Min(ty0 + 1, tilesY - 1);
|
||||
double ax = fx - tx0;
|
||||
double ay = fy - ty0;
|
||||
ax = Math.Clamp(ax, 0, 1);
|
||||
ay = Math.Clamp(ay, 0, 1);
|
||||
|
||||
byte val = srcData[y, x, 0];
|
||||
double v00 = luts[ty0, tx0, val];
|
||||
double v10 = luts[ty0, tx1, val];
|
||||
double v01 = luts[ty1, tx0, val];
|
||||
double v11 = luts[ty1, tx1, val];
|
||||
|
||||
double interpolated = v00 * (1 - ax) * (1 - ay) + v10 * ax * (1 - ay)
|
||||
+ v01 * (1 - ax) * ay + v11 * ax * ay;
|
||||
dstData[y, x, 0] = (byte)Math.Clamp((int)(interpolated + 0.5), 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Debug("ApplyCLAHE: ClipLimit={ClipLimit}, TileSize={TileSize}", clipLimit, tileSize);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user