mirror of
https://github.com/DarthAffe/ScreenCapture.NET.git
synced 2025-12-12 13:28:35 +00:00
Readded black-bar removal
This commit is contained in:
parent
bcb92eb422
commit
57462e9717
188
ScreenCapture.NET/Extensions/BlackBarDetection.cs
Normal file
188
ScreenCapture.NET/Extensions/BlackBarDetection.cs
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
namespace ScreenCapture.NET;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper-class for black-bar removal.
|
||||||
|
/// </summary>
|
||||||
|
public static class BlackBarDetection
|
||||||
|
{
|
||||||
|
#region IImage
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an image with black bars removed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="image">The image the bars are removed from.</param>
|
||||||
|
/// <param name="threshold">The threshold of "blackness" used to detect black bars. (e. g. Threshold 5 will consider a pixel of color [5,5,5] as black.)</param>
|
||||||
|
/// <param name="removeTop">A bool indicating if black bars should be removed at the top of the image.</param>
|
||||||
|
/// <param name="removeBottom">A bool indicating if black bars should be removed at the bottom of the image.</param>
|
||||||
|
/// <param name="removeLeft">A bool indicating if black bars should be removed on the left side of the image.</param>
|
||||||
|
/// <param name="removeRight">A bool indicating if black bars should be removed on the right side of the image.</param>
|
||||||
|
/// <returns>The image with black bars removed.</returns>
|
||||||
|
public static IImage RemoveBlackBars(this IImage image, int threshold = 0, bool removeTop = true, bool removeBottom = true, bool removeLeft = true, bool removeRight = true)
|
||||||
|
{
|
||||||
|
int top = removeTop ? CalculateTop(image, threshold) : 0;
|
||||||
|
int bottom = removeBottom ? CalculateBottom(image, threshold) : image.Height;
|
||||||
|
int left = removeLeft ? CalculateLeft(image, threshold) : 0;
|
||||||
|
int right = removeRight ? CalculateRight(image, threshold) : image.Width;
|
||||||
|
|
||||||
|
return image[left, top, right - left, bottom - top];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CalculateTop(IImage image, int threshold)
|
||||||
|
{
|
||||||
|
IImage.IImageRows rows = image.Rows;
|
||||||
|
for (int y = 0; y < rows.Count; y++)
|
||||||
|
{
|
||||||
|
IImage.IImageRow row = rows[y];
|
||||||
|
foreach (IColor color in row)
|
||||||
|
{
|
||||||
|
if ((color.R > threshold) || (color.G > threshold) || (color.B > threshold))
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CalculateBottom(IImage image, int threshold)
|
||||||
|
{
|
||||||
|
IImage.IImageRows rows = image.Rows;
|
||||||
|
for (int y = rows.Count - 1; y >= 0; y--)
|
||||||
|
{
|
||||||
|
IImage.IImageRow row = rows[y];
|
||||||
|
foreach (IColor color in row)
|
||||||
|
{
|
||||||
|
if ((color.R > threshold) || (color.G > threshold) || (color.B > threshold))
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CalculateLeft(IImage image, int threshold)
|
||||||
|
{
|
||||||
|
IImage.IImageColumns columns = image.Columns;
|
||||||
|
for (int x = 0; x < columns.Count; x++)
|
||||||
|
{
|
||||||
|
IImage.IImageColumn column = columns[x];
|
||||||
|
foreach (IColor color in column)
|
||||||
|
{
|
||||||
|
if ((color.R > threshold) || (color.G > threshold) || (color.B > threshold))
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CalculateRight(IImage image, int threshold)
|
||||||
|
{
|
||||||
|
IImage.IImageColumns columns = image.Columns;
|
||||||
|
for (int x = columns.Count - 1; x >= 0; x--)
|
||||||
|
{
|
||||||
|
IImage.IImageColumn column = columns[x];
|
||||||
|
foreach (IColor color in column)
|
||||||
|
{
|
||||||
|
if ((color.R > threshold) || (color.G > threshold) || (color.B > threshold))
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return columns.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region RefImage
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an image with black bars removed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="image">The image the bars are removed from.</param>
|
||||||
|
/// <param name="threshold">The threshold of "blackness" used to detect black bars. (e. g. Threshold 5 will consider a pixel of color [5,5,5] as black.)</param>
|
||||||
|
/// <param name="removeTop">A bool indicating if black bars should be removed at the top of the image.</param>
|
||||||
|
/// <param name="removeBottom">A bool indicating if black bars should be removed at the bottom of the image.</param>
|
||||||
|
/// <param name="removeLeft">A bool indicating if black bars should be removed on the left side of the image.</param>
|
||||||
|
/// <param name="removeRight">A bool indicating if black bars should be removed on the right side of the image.</param>
|
||||||
|
/// <returns>The image with black bars removed.</returns>
|
||||||
|
public static RefImage<TColor> RemoveBlackBars<TColor>(this RefImage<TColor> image, int threshold = 0, bool removeTop = true, bool removeBottom = true, bool removeLeft = true, bool removeRight = true)
|
||||||
|
where TColor : struct, IColor
|
||||||
|
{
|
||||||
|
int top = removeTop ? CalculateTop(image, threshold) : 0;
|
||||||
|
int bottom = removeBottom ? CalculateBottom(image, threshold) : image.Height;
|
||||||
|
int left = removeLeft ? CalculateLeft(image, threshold) : 0;
|
||||||
|
int right = removeRight ? CalculateRight(image, threshold) : image.Width;
|
||||||
|
|
||||||
|
return image[left, top, right - left, bottom - top];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CalculateTop<TColor>(this RefImage<TColor> image, int threshold)
|
||||||
|
where TColor : struct, IColor
|
||||||
|
{
|
||||||
|
RefImage<TColor>.ImageRows rows = image.Rows;
|
||||||
|
for (int y = 0; y < rows.Count; y++)
|
||||||
|
{
|
||||||
|
ReadOnlyRefEnumerable<TColor> row = rows[y];
|
||||||
|
foreach (TColor color in row)
|
||||||
|
{
|
||||||
|
if ((color.R > threshold) || (color.G > threshold) || (color.B > threshold))
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CalculateBottom<TColor>(this RefImage<TColor> image, int threshold)
|
||||||
|
where TColor : struct, IColor
|
||||||
|
{
|
||||||
|
RefImage<TColor>.ImageRows rows = image.Rows;
|
||||||
|
for (int y = rows.Count - 1; y >= 0; y--)
|
||||||
|
{
|
||||||
|
ReadOnlyRefEnumerable<TColor> row = rows[y];
|
||||||
|
foreach (TColor color in row)
|
||||||
|
{
|
||||||
|
if ((color.R > threshold) || (color.G > threshold) || (color.B > threshold))
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CalculateLeft<TColor>(this RefImage<TColor> image, int threshold)
|
||||||
|
where TColor : struct, IColor
|
||||||
|
{
|
||||||
|
RefImage<TColor>.ImageColumns columns = image.Columns;
|
||||||
|
for (int x = 0; x < columns.Count; x++)
|
||||||
|
{
|
||||||
|
ReadOnlyRefEnumerable<TColor> column = columns[x];
|
||||||
|
foreach (TColor color in column)
|
||||||
|
{
|
||||||
|
if ((color.R > threshold) || (color.G > threshold) || (color.B > threshold))
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CalculateRight<TColor>(this RefImage<TColor> image, int threshold)
|
||||||
|
where TColor : struct, IColor
|
||||||
|
{
|
||||||
|
RefImage<TColor>.ImageColumns columns = image.Columns;
|
||||||
|
for (int x = columns.Count - 1; x >= 0; x--)
|
||||||
|
{
|
||||||
|
ReadOnlyRefEnumerable<TColor> column = columns[x];
|
||||||
|
foreach (TColor color in column)
|
||||||
|
{
|
||||||
|
if ((color.R > threshold) || (color.G > threshold) || (color.B > threshold))
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return columns.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@ -1,141 +0,0 @@
|
|||||||
// ReSharper disable MemberCanBePrivate.Global
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace ScreenCapture.NET;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the configuration for the detection and removal of black bars around the screen image.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class BlackBarDetection
|
|
||||||
{
|
|
||||||
#region Properties & Fields
|
|
||||||
|
|
||||||
private readonly ICaptureZone _captureZone;
|
|
||||||
|
|
||||||
private int? _top;
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the size of the detected black bar at the top of the image.
|
|
||||||
/// </summary>
|
|
||||||
public int Top => _top ??= CalculateTop();
|
|
||||||
|
|
||||||
private int? _bottom;
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the size of the detected black bar at the bottom of the image.
|
|
||||||
/// </summary>
|
|
||||||
public int Bottom => _bottom ??= CalculateBottom();
|
|
||||||
|
|
||||||
private int? _left;
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the size of the detected black bar at the left of the image.
|
|
||||||
/// </summary>
|
|
||||||
public int Left => _left ??= CalculateLeft();
|
|
||||||
|
|
||||||
private int? _right;
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the size of the detected black bar at the right of the image.
|
|
||||||
/// </summary>
|
|
||||||
public int Right => _right ??= CalculateRight();
|
|
||||||
|
|
||||||
private int _theshold = 0;
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the threshold of "blackness" used to detect black bars. (e. g. Threshold 5 will consider a pixel of color [5,5,5] as black.) (default 0)
|
|
||||||
/// </summary>
|
|
||||||
public int Threshold
|
|
||||||
{
|
|
||||||
get => _theshold;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_theshold = value;
|
|
||||||
InvalidateCache();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Constructors
|
|
||||||
|
|
||||||
internal BlackBarDetection(ICaptureZone captureZone)
|
|
||||||
{
|
|
||||||
this._captureZone = captureZone;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalidates the cached values and recalculates <see cref="Top"/>, <see cref="Bottom"/>, <see cref="Left"/> and <see cref="Right"/>.
|
|
||||||
/// </summary>
|
|
||||||
public void InvalidateCache()
|
|
||||||
{
|
|
||||||
_top = null;
|
|
||||||
_bottom = null;
|
|
||||||
_left = null;
|
|
||||||
_right = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int CalculateTop()
|
|
||||||
{
|
|
||||||
//int threshold = Threshold;
|
|
||||||
//int stride = _captureZone.Stride;
|
|
||||||
//for (int row = 0; row < _captureZone.Height; row++)
|
|
||||||
//{
|
|
||||||
// Span<byte> data = new(_captureZone.Buffer, row * stride, stride);
|
|
||||||
// for (int i = 0; i < data.Length; i += 4)
|
|
||||||
// if ((data[i] > threshold) || (data[i + 1] > threshold) || (data[i + 2] > threshold))
|
|
||||||
// return row;
|
|
||||||
//}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int CalculateBottom()
|
|
||||||
{
|
|
||||||
//int threshold = Threshold;
|
|
||||||
//int stride = _captureZone.Stride;
|
|
||||||
//for (int row = _captureZone.Height - 1; row >= 0; row--)
|
|
||||||
//{
|
|
||||||
// Span<byte> data = new(_captureZone.Buffer, row * stride, stride);
|
|
||||||
// for (int i = 0; i < data.Length; i += 4)
|
|
||||||
// if ((data[i] > threshold) || (data[i + 1] > threshold) || (data[i + 2] > threshold))
|
|
||||||
// return (_captureZone.Height - 1) - row;
|
|
||||||
//}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int CalculateLeft()
|
|
||||||
{
|
|
||||||
//int threshold = Threshold;
|
|
||||||
//int stride = _captureZone.Stride;
|
|
||||||
//byte[] buffer = _captureZone.Buffer;
|
|
||||||
//for (int column = 0; column < _captureZone.Width; column++)
|
|
||||||
// for (int row = 0; row < _captureZone.Height; row++)
|
|
||||||
// {
|
|
||||||
// int offset = (stride * row) + (column * 4);
|
|
||||||
// if ((buffer[offset] > threshold) || (buffer[offset + 1] > threshold) || (buffer[offset + 2] > threshold))
|
|
||||||
// return column;
|
|
||||||
// }
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int CalculateRight()
|
|
||||||
{
|
|
||||||
//int threshold = Threshold;
|
|
||||||
//int stride = _captureZone.Stride;
|
|
||||||
//byte[] buffer = _captureZone.Buffer;
|
|
||||||
//for (int column = _captureZone.Width - 1; column >= 0; column--)
|
|
||||||
// for (int row = 0; row < _captureZone.Height; row++)
|
|
||||||
// {
|
|
||||||
// int offset = (stride * row) + (column * 4);
|
|
||||||
// if ((buffer[offset] > threshold) || (buffer[offset + 1] > threshold) || (buffer[offset + 2] > threshold))
|
|
||||||
// return (_captureZone.Width - 1) - column;
|
|
||||||
// }
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@ -2,6 +2,7 @@
|
|||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=communitytoolkit_002Ehighperformance/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=communitytoolkit_002Ehighperformance/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=directx/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=directx/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generic/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generic/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helper/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helper/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=model/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=model/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user