mirror of
https://github.com/unclshura/splitter.git
synced 2026-06-21 16:12:01 +00:00
101 lines
2.5 KiB
C#
101 lines
2.5 KiB
C#
namespace splitter.probe;
|
|
|
|
public sealed class FrameRotationDetector
|
|
{
|
|
private readonly Mat _gray;
|
|
private readonly Mat _gx;
|
|
private readonly Mat _gy;
|
|
private readonly Mat _mag;
|
|
private readonly Mat _angle;
|
|
|
|
private readonly float[] _hist;
|
|
|
|
private readonly int _w;
|
|
private readonly int _h;
|
|
private readonly int _bins;
|
|
|
|
public FrameRotationDetector(int width = 320, int height = 180, int bins = 36)
|
|
{
|
|
_w = width;
|
|
_h = height;
|
|
_bins = bins;
|
|
|
|
_gray = new Mat(height, width, MatType.CV_8UC1);
|
|
_gx = new Mat(height, width, MatType.CV_32F);
|
|
_gy = new Mat(height, width, MatType.CV_32F);
|
|
_mag = new Mat(height, width, MatType.CV_32F);
|
|
_angle = new Mat(height, width, MatType.CV_32F);
|
|
|
|
_hist = new float[bins]; // allocated once
|
|
}
|
|
|
|
public int GetRotation(Mat frame)
|
|
{
|
|
// 1. Grayscale
|
|
Cv2.CvtColor(frame, _gray, ColorConversionCodes.BGR2GRAY);
|
|
|
|
// 2. Sobel
|
|
Cv2.Sobel(_gray, _gx, MatType.CV_32F, 1, 0, 3);
|
|
Cv2.Sobel(_gray, _gy, MatType.CV_32F, 0, 1, 3);
|
|
|
|
// 3. Magnitude + angle
|
|
Cv2.CartToPolar(_gx, _gy, _mag, _angle, angleInDegrees: true);
|
|
|
|
// 4. Clear histogram
|
|
for (var i = 0; i < _bins; i++)
|
|
_hist[i] = 0;
|
|
|
|
var binSize = 180f / _bins;
|
|
|
|
unsafe
|
|
{
|
|
var anglePtr = (float*)_angle.Data;
|
|
var magPtr = (float*)_mag.Data;
|
|
|
|
var total = _w * _h;
|
|
|
|
for (var i = 0; i < total; i++)
|
|
{
|
|
var m = magPtr[i];
|
|
if (m < 5f) continue; // ignore weak gradients
|
|
|
|
var a = anglePtr[i];
|
|
if (a < 0) a += 360f;
|
|
a = a % 180f;
|
|
|
|
var bin = (int)(a / binSize);
|
|
if (bin < 0) bin = 0;
|
|
if (bin >= _bins) bin = _bins - 1;
|
|
|
|
_hist[bin] += m;
|
|
}
|
|
}
|
|
|
|
// 5. Energy around 0° vs 90°
|
|
float e0 = 0, e90 = 0;
|
|
var window = 3;
|
|
|
|
var bin0 = 0;
|
|
var bin90 = _bins / 2;
|
|
|
|
for (var i = -window; i <= window; i++)
|
|
{
|
|
e0 += _hist[Wrap(bin0 + i)];
|
|
e90 += _hist[Wrap(bin90 + i)];
|
|
}
|
|
|
|
// 6. Decide upright vs sideways
|
|
if (e90 > e0 * 1.6f)
|
|
return 90; // sideways
|
|
|
|
return 0; // upright (concert default)
|
|
}
|
|
|
|
private int Wrap(int b)
|
|
{
|
|
if (b < 0) return b + _bins;
|
|
if (b >= _bins) return b - _bins;
|
|
return b;
|
|
}
|
|
}
|