ASP.NET C#에서 비트 맵 이미지를 PNG 또는 GIF로 16 색 비 투명 회색 음영 이미지로 저장하려고합니다. 나는 팔레트를 만들어야하고 어떻게 든 이미지에 팔레트를 부착해야한다고 가정하지만 이것을 수행하는 방법을 모릅니다.ASP.NET에서 비트 맵을 16 색 그레이 스케일 GIF 또는 PNG로 저장하려면 어떻게해야합니까?
원본 이미지는 24 비트 컬러 비트 맵입니다.
ASP.NET C#에서 비트 맵 이미지를 PNG 또는 GIF로 16 색 비 투명 회색 음영 이미지로 저장하려고합니다. 나는 팔레트를 만들어야하고 어떻게 든 이미지에 팔레트를 부착해야한다고 가정하지만 이것을 수행하는 방법을 모릅니다.ASP.NET에서 비트 맵을 16 색 그레이 스케일 GIF 또는 PNG로 저장하려면 어떻게해야합니까?
원본 이미지는 24 비트 컬러 비트 맵입니다.
이것은 양자화라고 불리며 복잡합니다. 나는이 문제를 광범위하게 다루었으며, Octree 양자화와 사용자 정의 확산 알고리즘을 사용하여 최상의 결과를 얻었습니다.
A부터 B까지의 가장 빠른 지점은 grab my code (open-source, but $69 to download)이며 매우 간단한 API를 사용하여 색상 수를 16으로 설정하고 GIF 또는 PNG로 저장하십시오. 이 파일 시스템에 있다면 당신은 쿼리 문자열을 사용할 수 있습니다, 당신은 코드 숨김을 통해 작업을 수행하려는 경우 코드의 약 2 라인 수 ... 또는해야 :
image.bmp?format=gif&colors=16
이미지가 이미 그레이 스케일없는 경우 모듈의 ImageAttributes 클래스를 사용하여이를 수행 할 수 있습니다. 결과 GIF는 자동으로 회색 음영 팔레트를 갖습니다. 최소한의 작업, 훌륭한 결과.
HttpModule로 사용할 필요가 없음을 기억하십시오. 주로 이미지의 크기 조정, 수정 및 인코딩을위한 라이브러리입니다. 하지만,
는 의견을 읽고 내 의견에 따라 포인터 연산 오류를 패치 없음 디더링 :
당신은 여기에 내가 시작 무엇을, 자신의 롤하지 않으려면 완전 신뢰 환경보다 적게 원본을 실행하는 데 문제가있을 수 있습니다. 나는 수년에 걸쳐 많은 패치를 만들었고, 나는 그것들 모두를 기억하지 못합니다.
또 다른 가능성은 오픈 소스 코드를 뒤적 거리지 않고 Paint.Net을 다운로드하는 것입니다. 나는 그것이 그레이 스케일로 변환 할 수 있다고 믿지만, 나는 그것을 사용해야 할 필요가 있기 때문에 오랫동안 잘못 되었기 때문에 잘못 될 수있다.
일단 도구 세트가 있으면 실제로 그다지 어렵지 않습니다. 그리고 그 중 상당수를 구축했습니다. 필요한 항목은 다음과 같습니다.
팔레트는 쉽습니다. 회색 값은 빨강, 녹색 및 파랑에 대해 동일한 값을 갖는 색상이며, 16 색의 색상 간 밝기 단계가 동일 할 경우 해당 값은 0x00, 0x11, 0x22 등의 범위에서 0xFF까지입니다. 만들 어렵지 않아야합니다.
다음 단계는 이미지 색상을 팔레트 색상과 일치시키고 이러한 값의 바이트 배열을 만드는 것입니다. 가장 가까운 성냥을 이미 stackoverflow에서 사용할 수있게하는 몇 가지 방법이 있습니다.
How to compare Color object and get closest Color in an Color[]?
다음은 까다로운 부분을 제공 : 4 비트에 실제 이미지 데이터를 변환이 질문은 그들의 무리가있다.
한 가지 유의할 점은 이미지가 한 줄에 저장된다는 것과 그와 같은 선 ("스캔 라인"이라고도 함)은 반드시 이미지와 동일한 너비가 아니어야한다는 것입니다. 예를 들어, 픽셀 당 4 비트에서 각 바이트에 2 픽셀을 맞출 수 있으므로 논리적으로 보폭은 2로 나눈 값이됩니다. 그러나 너비가 고르지 않은 숫자 인 경우 각 행 끝에 바이트가 있습니다. 절반 만 채웠다. 시스템은 다음 라인의 첫 번째 픽셀을 거기에 두지 않습니다. 대신 단순히 공백으로 남겨 두었습니다. 또한 8 비트 또는 16 비트 이미지의 경우 스트라이드는 종종 스캔 라인을 4 바이트의 배수로 정렬합니다. 따라서 너비가 주사선 길이와 같다고 가정하지 마십시오.
필자는이 회신에 추가로 필요한 기능을 위해 필요한 최소 주사선 길이를 사용합니다. 이것은 8 배로 비트 길이를 나눈 값의 너비 배가되기 때문에 그 부분에 나머지가 있으면 1을 더한 것이므로 ((bpp * width) + 7)/8
으로 쉽게 계산할 수 있습니다.
그레이 스케일 팔레트를 생성 한 다음 이미지의 각 픽셀에 가장 가까운 팔레트 값을 포함하는 바이트 배열을 만들면 실제 8 비트에서 4 비트로 변환하는 모든 값을 갖게됩니다.
나는 주어진 비트 길이로 8 비트 데이터를 변환하는 함수를 작성했다. 따라서 4 비트 이미지에는 bitsLength=4
이 필요합니다.
BigEndian 매개 변수는 한 바이트 내의 값이 전환되는지 여부를 결정합니다. .Net 이미지에 대해서는 잘 모르겠지만, 1BPP 포맷은 빅 엔디 언 비트를 사용하는 반면, 가장 낮은 니블로 시작하는 4BPP 포맷을 사용했습니다.
/// <summary>
/// Converts given raw image data for a paletted 8-bit image to lower amount of bits per pixel.
/// </summary>
/// <param name="data8bit">The eight bit per pixel image data</param>
/// <param name="width">The width of the image</param>
/// <param name="height">The height of the image</param>
/// <param name="newBpp">The new amount of bits per pixel</param>
/// <param name="stride">Stride used in the original image data. Will be adjusted to the new stride value.</param>
/// <param name="bigEndian">Values inside a single byte are read from the largest to the smallest bit.</param>
/// <returns>The image data converted to the requested amount of bits per pixel.</returns>
private static Byte[] ConvertFrom8Bit(Byte[] data8bit, Int32 width, Int32 height, Int32 bitsLength, Boolean bigEndian)
{
if (newBpp > 8)
throw new ArgumentException("Cannot convert to bit format greater than 8!", "newBpp");
if (stride < width)
throw new ArgumentException("Stride is too small for the given width!", "stride");
if (data8bit.Length < stride * height)
throw new ArgumentException("Data given data is too small to contain an 8-bit image of the given dimensions", "data8bit");
Int32 parts = 8/bitsLength;
// Amount of bytes to write per width
Int32 stride = ((bpp * width) + 7)/8;
// Bit mask for reducing original data to actual bits maximum.
// Should not be needed if data is correct, but eh.
Int32 bitmask = (1 << bitsLength) - 1;
Byte[] dataXbit = new Byte[stride * height];
// Actual conversion porcess.
for (Int32 y = 0; y < height; y++)
{
for (Int32 x = 0; x < width; x++)
{
// This will hit the same byte multiple times
Int32 indexXbit = y * stride + x/parts;
// This will always get a new index
Int32 index8bit = y * width + x;
// Amount of bits to shift the data to get to the current pixel data
Int32 shift = (x % parts) * bitsLength;
// Reversed for big-endian
if (bigEndian)
shift = 8 - shift - bitsLength;
// Get data, reduce to bit rate, shift it and store it.
dataXbit[indexXbit] |= (Byte)((data8bit[index8bit] & bitmask) << shift);
}
}
return dataXbit;
}
다음 단계는 정확한 치수 및 픽셀 형식의 화상을 메모리에 보조 배열을 열고, 그것에 데이터를 덤프한다. 16 색 이미지의 픽셀 형식은 PixelFormat.Format4bppIndexed
입니다.
/// <summary>
/// Creates a bitmap based on data, width, height, stride and pixel format.
/// </summary>
/// <param name="sourceData">Byte array of raw source data</param>
/// <param name="width">Width of the image</param>
/// <param name="height">Height of the image</param>
/// <param name="stride">Scanline length inside the data</param>
/// <param name="pixelFormat"></param>
/// <param name="palette">Color palette</param>
/// <returns>The new image</returns>
public static Bitmap BuildImage(Byte[] sourceData, Int32 width, Int32 height, Int32 stride, PixelFormat pixelFormat, Color[] palette)
{
if (width == 0 || height == 0)
return null;
Bitmap newImage = new Bitmap(width, height, pixelFormat);
BitmapData targetData = newImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, newImage.PixelFormat);
CopyMemory(targetData.Scan0, sourceData, sourceData.Length, stride, targetData.Stride);
newImage.UnlockBits(targetData);
// For 8-bit images, set the palette.
if ((pixelFormat == PixelFormat.Format8bppIndexed || pixelFormat == PixelFormat.Format4bppIndexed) && palette != null)
{
ColorPalette pal = newImage.Palette;
for (Int32 i = 0; i < pal.Entries.Length; i++)
if (i < palette.Length)
pal.Entries[i] = palette[i];
newImage.Palette = pal;
}
return newImage;
}
그리고 마지막으로 그 기능을 사용하는 메모리를 복사합니다. 보시다시피이 메서드는 지정된 스트라이드를 사용하여 줄 단위로 복사하므로 .Net 프레임 워크가 만드는 Bitmap
에 사용되는 내부 보폭은 무시할 수 있습니다. 그것은 어쨌거나 같거나 클 것입니다.
public static void CopyMemory(IntPtr target, Byte[] sourceBytes, Int32 length, Int32 origStride, Int32 targetStride)
{
IntPtr unmanagedPointer = Marshal.AllocHGlobal(sourceBytes.Length);
Marshal.Copy(sourceBytes, 0, unmanagedPointer, sourceBytes.Length);
CopyMemory(target, unmanagedPointer, length, origStride, targetStride);
Marshal.FreeHGlobal(unmanagedPointer);
}
public static void CopyMemory(IntPtr target, IntPtr source, Int32 length, Int32 origStride, Int32 targetStride)
{
IntPtr sourcePos = source;
IntPtr destPos = target;
Int32 minStride = Math.Min(origStride, targetStride);
Byte[] imageData = new Byte[targetStride];
while (length >= origStride && length > 0)
{
Marshal.Copy(sourcePos, imageData, 0, minStride);
Marshal.Copy(imageData, 0, destPos, targetStride);
length -= origStride;
sourcePos = new IntPtr(sourcePos.ToInt64() + origStride);
destPos = new IntPtr(destPos.ToInt64() + targetStride);
}
if (length > 0)
{
Marshal.Copy(sourcePos, imageData, 0, length);
Marshal.Copy(imageData, 0, destPos, length);
}
}
octree 및 기타 algos를 포함한 더 많은 무료 예제는 codeplex.com 및 code.msdn.com을 확인하십시오. –
어떤 종류의 "오픈 소스"소프트웨어를 다운로드하려면 $ 69가 필요합니까? – TAG
@ 환불 불가 - 예, MSDN에는 일부가 있지만 codebetter.com보다 나이가 들리고 버그가 있습니다. 기사를 읽으면 그가 시작했다. @TAG 많은 오픈 소스 소프트웨어. 많은 리눅스 배포판처럼. 오픈 소스는 무료 프로젝트가 아니라 다른 프로젝트의 일부로 재배포 가능합니다. 나는 라이센스 번거 로움을 싫어, 나는 다른 devs도 할 그림. –