SixLabors/ImageSharp · error · NotSupportedException

Matrix is singular and cannot be solve

Error message

Matrix is singular and cannot be solve

What it means

GaussianEliminationSolver.TransformToRowEchelonForm solves linear systems via Gaussian elimination; if it cannot find a nonzero pivot in a column, the matrix is singular and the system has no unique solution. Since NotSupportedException conveys 'this operation cannot proceed', the solver aborts rather than returning garbage.

Solutions

  1. Provide 4 non-degenerate (non-collinear, distinct) source/destination point pairs
  2. Deduplicate and validate control points before building the transform
  3. Catch NotSupportedException from Solve and fall back to an affine transform, which needs fewer points
  4. If points come from feature detection, apply an epsilon check on the area of the quad formed by the points

Example fix

// before
var matrix = ProjectiveTransformBuilder.DefineQuad(sourcePoints, destPoints); // collinear points
// after
static bool IsValidQuad(PointF[] p) =>
    Math.Abs(((p[1].X - p[0].X) * (p[2].Y - p[0].Y)) - ((p[2].X - p[0].X) * (p[1].Y - p[0].Y))) > float.Epsilon;
if (!IsValidQuad(sourcePoints) || !IsValidQuad(destPoints))
{
    throw new InvalidOperationException("Projective transform requires non-collinear points.");
}
var matrix = ProjectiveTransformBuilder.DefineQuad(sourcePoints, destPoints);
Defensive patterns

Strategy: validation

Validate before calling

static float QuadArea(PointF a, PointF b, PointF c, PointF d) =>
    MathF.Abs((b.X - a.X) * (c.Y - a.Y) - (c.X - a.X) * (b.Y - a.Y))
  + MathF.Abs((c.X - a.X) * (d.Y - a.Y) - (d.X - a.X) * (c.Y - a.Y));
if (QuadArea(p0, p1, p2, p3) < 1e-3f) throw new InvalidOperationException("Points are degenerate/collinear.");

Type guard

static bool ArePointsDistinctAndNonCollinear(PointF[] pts) =>
    pts.Length == 4
    && pts.Distinct().Count() == 4
    && MathF.Abs((pts[1].X - pts[0].X) * (pts[2].Y - pts[0].Y) - (pts[2].X - pts[0].X) * (pts[1].Y - pts[0].Y)) > float.Epsilon;

Try / catch

try
{
    var m = ProjectiveTransformBuilder.DefineQuad(src, dst);
}
catch (NotSupportedException ex) when (ex.Message.Contains("singular"))
{
    // fall back to affine transform or request better point correspondences
}

Prevention

When it happens

Trigger: Calling Solve (used by ProjectiveTransformBuilder to derive projective matrices from point correspondences) with degenerate/ collinear control points so the elimination hits a zero pivot column.

Common situations: Using ProjectiveTransformBuilder with 4 coplanar/collinear source points; supplying duplicate destination points; computing transforms from auto-detected corner points that are degenerate (e.g. straight-line feature matches).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of SixLabors/ImageSharp@59ce6af6fc (2026-09-13). Data as JSON: /api/errors/c2a446b22dd88fb0. Report an issue: GitHub.

Appendix: source

Thrown at src/ImageSharp/Processing/Processors/Transforms/Linear/GaussianEliminationSolver.cs:50

        int rowCount = matrix[0].Length;
        int pivotRow = 0;
        for (int pivotCol = 0; pivotCol < colCount; pivotCol++)
        {
            double maxValue = double.Abs(matrix[pivotRow][pivotCol]);
            int maxIndex = pivotRow;
            for (int r = pivotRow + 1; r < rowCount; r++)
            {
                double value = double.Abs(matrix[r][pivotCol]);
                if (value > maxValue)
                {
                    maxIndex = r;
                    maxValue = value;
                }
            }

            if (matrix[maxIndex][pivotCol] == 0)
            {
                throw new NotSupportedException("Matrix is singular and cannot be solve");
            }

            (matrix[pivotRow], matrix[maxIndex]) = (matrix[maxIndex], matrix[pivotRow]);
            (result[pivotRow], result[maxIndex]) = (result[maxIndex], result[pivotRow]);

            for (int r = pivotRow + 1; r < rowCount; r++)
            {
                double fraction = matrix[r][pivotCol] / matrix[pivotRow][pivotCol];
                for (int c = pivotCol + 1; c < colCount; c++)
                {
                    matrix[r][c] -= matrix[pivotRow][c] * fraction;
                }

                result[r] -= result[pivotRow] * fraction;
                matrix[r][pivotCol] = 0;
            }

            pivotRow++;

View on GitHub (pinned to 59ce6af6fc)