비트맵 이미지의 특정 픽셀 색상 찾기
에서 로드한 WPF 비트맵이미지가 있어요다음과 같은 JPG 파일:
this.m_image1.Source = new BitmapImage(new Uri(path));
나는 특정 지점에서의 색상에 대해 질문하고 싶다.예를 들어 픽셀(65,32)에서의 RGB 값은 얼마입니까?
이거 어떻게 해야 되지?저는 다음과 같은 접근방식을 취했습니다.
ImageSource ims = m_image1.Source;
BitmapImage bitmapImage = (BitmapImage)ims;
int height = bitmapImage.PixelHeight;
int width = bitmapImage.PixelWidth;
int nStride = (bitmapImage.PixelWidth * bitmapImage.Format.BitsPerPixel + 7) / 8;
byte[] pixelByteArray = new byte[bitmapImage.PixelHeight * nStride];
bitmapImage.CopyPixels(pixelByteArray, nStride, 0);
원숭이를 보는 눈이 조금 있긴 하지만 원숭이는 이 코드를 계속 사용합니다.어쨌든, 이 바이트 배열을 RGB 값으로 변환하는 간단한 방법이 있을까요?
다차원 배열을 사용하여 C#의 픽셀을 조작하는 방법은 다음과 같습니다.
[StructLayout(LayoutKind.Sequential)]
public struct PixelColor
{
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
}
public PixelColor[,] GetPixels(BitmapSource source)
{
if(source.Format!=PixelFormats.Bgra32)
source = new FormatConvertedBitmap(source, PixelFormats.Bgra32, null, 0);
int width = source.PixelWidth;
int height = source.PixelHeight;
PixelColor[,] result = new PixelColor[width, height];
source.CopyPixels(result, width * 4, 0);
return result;
}
사용방법:
var pixels = GetPixels(image);
if(pixels[7, 3].Red > 4)
{
...
}
는, 「」를 작성하는 것 에는, 한 코드가 합니다.WriteableBitmap , 으면 안 돼요.
public void PutPixels(WriteableBitmap bitmap, PixelColor[,] pixels, int x, int y)
{
int width = pixels.GetLength(0);
int height = pixels.GetLength(1);
bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, width*4, x, y);
}
따라서 다음과 같이 됩니다.
var pixels = new PixelColor[4, 3];
pixels[2,2] = new PixelColor { Red=128, Blue=0, Green=255, Alpha=255 };
PutPixels(bitmap, pixels, 7, 7);
이 코드는 비트맵이 다른 형식으로 도착하면 Bgra32로 변환됩니다.이것은 일반적으로 빠르지만 경우에 따라서는 성능 병목현상이 발생할 수 있습니다.이 경우 이 기술은 기본 입력 형식에 더 가깝게 수정될 수 있습니다.
갱신하다
★★BitmapSource.CopyPixels2번입니다.배열을 1차원과 2차원으로 변환해야 합니다.다음 확장 메서드로 문제를 해결할 수 있습니다.
public static class BitmapSourceHelper
{
#if UNSAFE
public unsafe static void CopyPixels(this BitmapSource source, PixelColor[,] pixels, int stride, int offset)
{
fixed(PixelColor* buffer = &pixels[0, 0])
source.CopyPixels(
new Int32Rect(0, 0, source.PixelWidth, source.PixelHeight),
(IntPtr)(buffer + offset),
pixels.GetLength(0) * pixels.GetLength(1) * sizeof(PixelColor),
stride);
}
#else
public static void CopyPixels(this BitmapSource source, PixelColor[,] pixels, int stride, int offset)
{
var height = source.PixelHeight;
var width = source.PixelWidth;
var pixelBytes = new byte[height * width * 4];
source.CopyPixels(pixelBytes, stride, 0);
int y0 = offset / width;
int x0 = offset - width * y0;
for(int y=0; y<height; y++)
for(int x=0; x<width; x++)
pixels[x+x0, y+y0] = new PixelColor
{
Blue = pixelBytes[(y*width + x) * 4 + 0],
Green = pixelBytes[(y*width + x) * 4 + 1],
Red = pixelBytes[(y*width + x) * 4 + 2],
Alpha = pixelBytes[(y*width + x) * 4 + 3],
};
}
#endif
}
여기에는 다음 두 가지 구현이 있습니다.첫 번째는 빠르지만 안전하지 않은 코드를 사용하여 IntPtr을 어레이로 가져옵니다(/unsafe 옵션을 사용하여 컴파일해야 함).두 번째는 속도가 느리지만 안전하지 않은 코드가 필요하지 않습니다.내 코드에 안전하지 않은 버전을 사용합니다.
WritePixels는 2차원 배열을 지원하므로 확장 방식은 필요하지 않습니다.
편집: Jerry가 코멘트에서 지적했듯이 메모리 레이아웃 때문에 2차원 배열은 먼저 수직 좌표를 가집니다.즉, 픽셀로 치수를 지정해야 합니다.높이, 폭은 픽셀[폭, 높이]가 아니며 픽셀[y,x]로 지정됩니다.
PixelColor 구조를 유니언으로 선언할 수도 있다는 Ray의 답변을 덧붙이고 싶습니다.
[StructLayout(LayoutKind.Explicit)]
public struct PixelColor
{
// 32 bit BGRA
[FieldOffset(0)] public UInt32 ColorBGRA;
// 8 bit components
[FieldOffset(0)] public byte Blue;
[FieldOffset(1)] public byte Green;
[FieldOffset(2)] public byte Red;
[FieldOffset(3)] public byte Alpha;
}
그러면 개별 바이트 구성 요소 외에도 UInit32 BGRA(빠른 픽셀 액세스 또는 복사)에 액세스할 수 있습니다.
결과 바이트 배열의 해석은 소스 비트맵의 픽셀 형식에 따라 다르지만, 가장 단순한 32비트 ARGB 이미지의 경우 각 픽셀은 바이트 배열의 4바이트로 구성됩니다.첫 번째 픽셀은 다음과 같이 해석됩니다.
alpha = pixelByteArray[0];
red = pixelByteArray[1];
green = pixelByteArray[2];
blue = pixelByteArray[3];
이미지 내의 각 픽셀을 처리하려면 행과 열을 이동하는 중첩된 루프를 생성하여 각 픽셀의 바이트 수만큼 인덱스 변수를 증가시킬 수 있습니다.
일부 비트맵 유형은 여러 픽셀을 하나의 바이트로 결합합니다.예를 들어, 흑백 이미지는 각 바이트에 8개의 픽셀을 채웁니다.픽셀당 24/32비트 이외의 이미지(간단한 이미지)를 취급해야 한다면 비트맵의 기본 바이너리 구조를 다루는 좋은 책을 추천합니다.
나는 Ray의 답변을 개선하고 싶다. - 코멘트가 충분하지 않다.>:(이 버전은 안전한/관리된 버전과 안전하지 않은 버전의 효율성 중 가장 좋은 점을 모두 갖추고 있습니다.또한, 나는 더 보폭으로 패스하는 것을 없앴다.CopyPixels의 넷 문서에는 버퍼가 아닌 비트맵의 스트라이드라고 되어 있습니다.오해의 소지가 있어 함수 내부에서 계산할 수 있습니다.PixelColor 어레이는 비트맵과 같은 스트라이드여야 하기 때문에 (단일 복사 호출로 실행할 수 있도록) 함수에 새로운 어레이를 작성하는 것도 의미가 있습니다.식은 죽 먹기다.
public static PixelColor[,] CopyPixels(this BitmapSource source)
{
if (source.Format != PixelFormats.Bgra32)
source = new FormatConvertedBitmap(source, PixelFormats.Bgra32, null, 0);
PixelColor[,] pixels = new PixelColor[source.PixelWidth, source.PixelHeight];
int stride = source.PixelWidth * ((source.Format.BitsPerPixel + 7) / 8);
GCHandle pinnedPixels = GCHandle.Alloc(pixels, GCHandleType.Pinned);
source.CopyPixels(
new Int32Rect(0, 0, source.PixelWidth, source.PixelHeight),
pinnedPixels.AddrOfPinnedObject(),
pixels.GetLength(0) * pixels.GetLength(1) * 4,
stride);
pinnedPixels.Free();
return pixels;
}
는 모든 를 다 더 예를 . - . - 시험해 보다.
로 하는 (Magic 96 DPI)
또한 이 WPF 전술과 다음을 비교했습니다.
- 그래픽스를 사용한 GDI(system.drawing)
- GDI32에서 직접 GetPixel을 호출하여 상호 운용합니다.DLL
을 위하여
GDI 10 10 、 Interop 다 15 、
WPF 를 사용하고 있는 경우는, 이것을 사용해 픽셀의 색을 얻는 것이 훨씬 좋습니다.
public static class GraphicsHelpers
{
public static readonly float DpiX;
public static readonly float DpiY;
static GraphicsHelpers()
{
using (var g = Graphics.FromHwnd(IntPtr.Zero))
{
DpiX = g.DpiX;
DpiY = g.DpiY;
}
}
public static Color WpfGetPixel(double x, double y, FrameworkElement AssociatedObject)
{
var renderTargetBitmap = new RenderTargetBitmap(
(int)AssociatedObject.ActualWidth,
(int)AssociatedObject.ActualHeight,
DpiX, DpiY, PixelFormats.Default);
renderTargetBitmap.Render(AssociatedObject);
if (x <= renderTargetBitmap.PixelWidth && y <= renderTargetBitmap.PixelHeight)
{
var croppedBitmap = new CroppedBitmap(
renderTargetBitmap, new Int32Rect((int)x, (int)y, 1, 1));
var pixels = new byte[4];
croppedBitmap.CopyPixels(pixels, 4, 0);
return Color.FromArgb(pixels[3], pixels[2], pixels[1], pixels[0]);
}
return Colors.Transparent;
}
}
소견:
이 코드(Edit: by Ray Burns)를 사용하려고 하는데 어레이의 등급에 대한 오류가 나타나면 다음과 같이 확장 방법을 편집해 보십시오.
public static void CopyPixels(this BitmapSource source, PixelColor[,] pixels, int stride, int offset, bool dummy)
후, 「」를 호출합니다.CopyPixels하다
source.CopyPixels(result, width * 4, 0, false);
문제는 확장 방식이 기존 방식과 다르지 않은 경우 원래 방식이 호출된다는 것입니다.이거는 아무래도PixelColor[,]Array뿐만 아니라.
같은 문제가 생겼다면 이게 도움이 됐으면 좋겠어요.
픽셀 색상을 하나만 원하는 경우:
using System.Windows.Media;
using System.Windows.Media.Imaging;
...
public static Color GetPixelColor(BitmapSource source, int x, int y)
{
Color c = Colors.White;
if (source != null)
{
try
{
CroppedBitmap cb = new CroppedBitmap(source, new Int32Rect(x, y, 1, 1));
var pixels = new byte[4];
cb.CopyPixels(pixels, 4, 0);
c = Color.FromRgb(pixels[2], pixels[1], pixels[0]);
}
catch (Exception) { }
}
return c;
}
훨씬 더 간단하죠.데이터를 복사할 필요가 없습니다. 을 사용하다하지만 여기에는 대가가 따른다: 포인터와unsafe특정 상황에서 빠르고 쉽게 할 가치가 있는지 여부를 판단합니다(단, 이미지 조작을 다른 안전하지 않은 클래스에 넣으면 나머지 프로그램은 영향을 받지 않습니다).
var bitmap = new WriteableBitmap(image);
data = (Pixel*)bitmap.BackBuffer;
stride = bitmap.BackBufferStride / 4;
bitmap.Lock();
// getting a pixel value
Pixel pixel = (*(data + y * stride + x));
bitmap.Unlock();
어디에
[StructLayout(LayoutKind.Explicit)]
protected struct Pixel {
[FieldOffset(0)]
public byte B;
[FieldOffset(1)]
public byte G;
[FieldOffset(2)]
public byte R;
[FieldOffset(3)]
public byte A;
}
에러 체크(포맷이 실제로 BGRA인지 아닌지의 여부 및 그렇지 않은 경우의 처리)는 독자에게 맡겨집니다.
색 구성 요소는 바이트 배열로 가져올 수 있습니다.먼저 32비트의 픽셀을 어레이에 복사하여 4배 큰 크기의 8비트 어레이로 변환합니다.
int[] pixelArray = new int[stride * source.PixelHeight];
source.CopyPixels(pixelArray, stride, 0);
// byte[] colorArray = new byte[pixelArray.Length];
// EDIT:
byte[] colorArray = new byte[pixelArray.Length * 4];
for (int i = 0; i < colorArray.Length; i += 4)
{
int pixel = pixelArray[i / 4];
colorArray[i] = (byte)(pixel >> 24); // alpha
colorArray[i + 1] = (byte)(pixel >> 16); // red
colorArray[i + 2] = (byte)(pixel >> 8); // green
colorArray[i + 3] = (byte)(pixel); // blue
}
// colorArray is an array of length 4 times more than the actual number of pixels
// in the order of [(ALPHA, RED, GREEN, BLUE), (ALPHA, RED...]
언급URL : https://stackoverflow.com/questions/1176910/finding-specific-pixel-colors-of-a-bitmapimage
'source' 카테고리의 다른 글
| Swift 4 모드에서 Swift 3 @objc 추론 사용은 권장되지 않습니다. (0) | 2023.04.17 |
|---|---|
| Git에서 특정 커밋을 병합하는 방법 (0) | 2023.04.17 |
| 사용되지 않는 크기의 대체With Font: iOS 7? (0) | 2023.04.17 |
| 삽입 명령 실행 및 SQL에 삽입된 ID 반환 (0) | 2023.04.17 |
| 아이폰 네비게이션바 제목 텍스트 색상 (0) | 2023.04.17 |