32bpp ARGB 모드에서 System.Drawing.Bitmap
이 있다고 가정합니다. 큰 비트 맵이지만 대부분 중간에 어딘가에 비교적 작은 이미지가있는 대부분 투명 픽셀입니다.자동으로 비트 맵을 최소 크기로 트림합니까?
"진짜"이미지의 테두리를 감지하는 빠른 알고리즘은 무엇입니까? 그래서 주변의 모든 투명 픽셀을 잘라낼 수 있습니까?
또는 여기에 사용할 수있는 .Net의 기능이 있습니까?
32bpp ARGB 모드에서 System.Drawing.Bitmap
이 있다고 가정합니다. 큰 비트 맵이지만 대부분 중간에 어딘가에 비교적 작은 이미지가있는 대부분 투명 픽셀입니다.자동으로 비트 맵을 최소 크기로 트림합니까?
"진짜"이미지의 테두리를 감지하는 빠른 알고리즘은 무엇입니까? 그래서 주변의 모든 투명 픽셀을 잘라낼 수 있습니까?
또는 여기에 사용할 수있는 .Net의 기능이 있습니까?
기본 아이디어는 이미지의 모든 픽셀을 확인하여 이미지의 위쪽, 왼쪽, 오른쪽 및 아래쪽 경계를 찾는 것입니다. 이 작업을 효율적으로 수행하려면 GetPixel
메서드를 사용하지 마십시오. 속도가 매우 느립니다. 대신 LockBits
을 사용하십시오. 여기
static Bitmap TrimBitmap(Bitmap source)
{
Rectangle srcRect = default(Rectangle);
BitmapData data = null;
try
{
data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] buffer = new byte[data.Height * data.Stride];
Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
int xMin = int.MaxValue;
int xMax = 0;
int yMin = int.MaxValue;
int yMax = 0;
for (int y = 0; y < data.Height; y++)
{
for (int x = 0; x < data.Width; x++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
if (x < xMin) xMin = x;
if (x > xMax) xMax = x;
if (y < yMin) yMin = y;
if (y > yMax) yMax = y;
}
}
}
if (xMax < xMin || yMax < yMin)
{
// Image is empty...
return null;
}
srcRect = Rectangle.FromLTRB(xMin, yMin, xMax, yMax);
}
finally
{
if (data != null)
source.UnlockBits(data);
}
Bitmap dest = new Bitmap(srcRect.Width, srcRect.Height);
Rectangle destRect = new Rectangle(0, 0, srcRect.Width, srcRect.Height);
using (Graphics graphics = Graphics.FromImage(dest))
{
graphics.DrawImage(source, destRect, srcRect, GraphicsUnit.Pixel);
}
return dest;
}
,하지만 난 GDI + 전문가가 아니라, 그래서 내가 더 연구하지 않고 할 수있는 최선의
편집 :
static Bitmap TrimBitmap(Bitmap source)
{
Rectangle srcRect = default(Rectangle);
BitmapData data = null;
try
{
data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] buffer = new byte[data.Height * data.Stride];
Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
int xMin = int.MaxValue,
xMax = int.MinValue,
yMin = int.MaxValue,
yMax = int.MinValue;
bool foundPixel = false;
// Find xMin
for (int x = 0; x < data.Width; x++)
{
bool stop = false;
for (int y = 0; y < data.Height; y++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
xMin = x;
stop = true;
foundPixel = true;
break;
}
}
if (stop)
break;
}
// Image is empty...
if (!foundPixel)
return null;
// Find yMin
for (int y = 0; y < data.Height; y++)
{
bool stop = false;
for (int x = xMin; x < data.Width; x++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
yMin = y;
stop = true;
break;
}
}
if (stop)
break;
}
// Find xMax
for (int x = data.Width - 1; x >= xMin; x--)
{
bool stop = false;
for (int y = yMin; y < data.Height; y++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
xMax = x;
stop = true;
break;
}
}
if (stop)
break;
}
// Find yMax
for (int y = data.Height - 1; y >= yMin; y--)
{
bool stop = false;
for (int x = xMin; x <= xMax; x++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
yMax = y;
stop = true;
break;
}
}
if (stop)
break;
}
srcRect = Rectangle.FromLTRB(xMin, yMin, xMax, yMax);
}
finally
{
if (data != null)
source.UnlockBits(data);
}
Bitmap dest = new Bitmap(srcRect.Width, srcRect.Height);
Rectangle destRect = new Rectangle(0, 0, srcRect.Width, srcRect.Height);
using (Graphics graphics = Graphics.FromImage(dest))
{
graphics.DrawImage(source, destRect, srcRect, GraphicsUnit.Pixel);
}
return dest;
}
상당한 이득이되지 않습니다 : yMax 인
EDIT2에 저장 Y : 여기에 위의 접근 방식의 구현이다 여전히 대부분의 픽셀을 스캔합니다. 그러나 크기가 클 경우 불투명 한 부분 주위의 직사각형 만 스캔됩니다.
가장 실용적인 접근법처럼 보인다. 나는 이것보다 더 좋아 보이지 않는다. LockBits 메서드를 사용한 멋진 팁. +1 –
BTW, 나는 Graphics를 사용하지 않고 이미지를 잘라내는 더 간단한 방법이 있음을 깨달았다 :'return source.Clone (srcRect, source.PixelFormat);' –
위대한 해답은 매우 유용하지만, 내 이미지를 발견했다. 한 픽셀 씩 너무 많이 클리핑됩니다. 논리적으로 당신의 말이 맞는 것 같지만, 전화를 ** Rectangle.FromLTRB **로 변경하여 ** srcRect = Rectangle.FromLTRB (xMin, yMin, xMax + 1, yMax + 1) **로 바꿨습니다. –
내가 나누기 & 정복 방법 제안하고 싶습니다 :
당신의 5 번째 포인트가 잘못되었다고 생각합니다. 투명하지 않은 픽셀을 가진 몇 가지 별개의 영역이있을 수 있습니다. 따라서 잘라진 라인에 투명하지 않은 픽셀이 없기 때문에 아무런 의미가 없습니다. –
감사합니다. bjoernz, 예 : 바이너리 검색이 가능합니다. 내 이미지에 항상 작동하는 것은 아닙니다. 예를 들어 공백으로 구분 된 두 개의 이미지가있을 수 있습니다. – Blorgbeard
컷오프가 직선입니까? 그렇다면 L-> R 및 T-> B에서 픽셀을 읽는 것이 매우 빠르게 작동합니다. –
사각형 일 경우 더 많은 시간을 절약 할 수 있으며 4 개 측면 모두에서 중심에서 바깥 쪽을 검색 할 수 있습니다 (적어도 픽셀 조사를 줄임). –
작은 포함 된 이미지에도 투명 픽셀이 포함될 수 있습니까? –