缩小图像大小以实现紧凑的框架
Reducing an image size for compact framework
我正在寻找一种方法,可以减少我从屏幕上捕获的图像的大小(以字节为单位),而不是像远程桌面一样发送到另一台电脑(这实际上是我的项目目的,顺便说一句,这部分已经完成,我在压缩图像时遇到了一些麻烦)。
程序必须运行的计算机有 Compact Framework 2.0 (Windows CE 6.0),我无法更改(@work 我们在某些机器上使用这些计算机,所以更改有点困难 OS 和框架)。
无论如何,这是我用来捕获屏幕的方法,然后是我用来更改像素颜色深度的实际方法。
int DefaultMaxScreenDiskSize = 212500;
private bool StreamScreen()
{
Rectangle bounds = Screen.PrimaryScreen.Bounds;
IntPtr hdc = GetDC(IntPtr.Zero);
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format16bppRgb555);
using (MemoryStream ms = new MemoryStream())
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
IntPtr dstHdc = graphics.GetHdc();
BitBlt(dstHdc, 0, 0, bounds.Width, bounds.Height, hdc, 0, 0, Enums.RasterOperation.SRC_COPY);
graphics.ReleaseHdc(dstHdc);
}
bitmap.Save(ms, ImageFormat.Png);
int length = ms.ToArray().Length;
if (length > DefaultMaxScreenDiskSize)
{
Bitmap nBitmap = ApplyDecreaseColourDepth(128, bitmap);
nBitmap.Save(ms, ImageFormat.Png);
}
using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
currentImgHash = Convert.ToBase64String(sha1.ComputeHash(ms.ToArray()));
if (oldImgHash != currentImgHash || ForceScreenRefresh)
{
ReleaseDC(IntPtr.Zero, hdc);
if (ms.ToArray() != null)
{
while (!SendScreen(ZlibStream.CompressBuffer(ms.ToArray()))) ;
oldImgHash = currentImgHash;
ForceScreenRefresh = false;
return true;
}
}
}
return false;
}
这是我使用的另一种方法,即改变像素深度的方法。但是,它有点慢,而且对系统来说很重,所以我不知道如何改变它。
public Bitmap ApplyDecreaseColourDepth(int offset, Bitmap bitmapImage)
{
for (int y = 0; y < bitmapImage.Height; y++)
{
for (int x = 0; x < bitmapImage.Width; x++)
{
Color pixelColor = bitmapImage.GetPixel(x, y);
int R = Math.Max(0, (pixelColor.R + offset / 2) / offset * offset - 1);
int G = Math.Max(0, (pixelColor.G + offset / 2) / offset * offset - 1);
int B = Math.Max(0, (pixelColor.B + offset / 2) / offset * offset - 1);
bitmapImage.SetPixel(x, y, Color.FromArgb(R, G, B));
}
}
return bitmapImage;
}
知道如何更改深度像素或压缩位图吗?我试图压缩图像,但(这是一个 .PNG)它只改变了几个字节(例如正常大小 ms.length = 38689,压缩它 ms.length = 38489)
很遗憾,我还没有解决压缩问题的方法。但我记得我前一段时间看到的一篇来自 CodeProject 的文章,它准确地描述了 GetPixel()
和 SetPixel()
非常慢的问题。这个限制可以通过下面的代码来克服。
为了使其编译,您需要在相应项目的构建选项中允许不安全代码。
public unsafe Bitmap ApplyDecreaseColourDepth(int offset, Bitmap bitmapImage)
{
BitmapData data = bitmapImage.LockBits(
new Rectangle(0, 0, bitmapImage.Width, bitmapImage.Height),
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
const int bytesPerPixel = 3;
Byte* scan0 = (Byte*)data.Scan0.ToPointer();
Int32 stride = data.Stride;
for (int y = 0; y < bitmapImage.Height; y++)
{
Byte* row = scan0 + (y * stride);
for (int x = 0; x < bitmapImage.Width; x++)
{
Int32 bIndex = x * bytesPerPixel;
Int32 gIndex = bIndex + 1;
Int32 rIndex = bIndex + 2;
Byte pixelR = row[rIndex];
Byte pixelG = row[gIndex];
Byte pixelB = row[bIndex];
row[rIndex] = (Byte)Math.Max(0, (pixelR + offset / 2) / offset * offset - 1);
row[gIndex] = (Byte)Math.Max(0, (pixelG + offset / 2) / offset * offset - 1);
row[bIndex] = (Byte)Math.Max(0, (pixelB + offset / 2) / offset * offset - 1);
}
}
bitmapImage.UnlockBits(data);
return bitmapImage;
}
这是未经测试的,所以请在将其投入生产之前进行测试!可以在 https://www.codeproject.com/Articles/617613/Fast-pixel-operations-in-NET-with-and-without-unsa
下找到我正在谈论的文章(以及我曾经更改过您的代码的文章)
我正在寻找一种方法,可以减少我从屏幕上捕获的图像的大小(以字节为单位),而不是像远程桌面一样发送到另一台电脑(这实际上是我的项目目的,顺便说一句,这部分已经完成,我在压缩图像时遇到了一些麻烦)。
程序必须运行的计算机有 Compact Framework 2.0 (Windows CE 6.0),我无法更改(@work 我们在某些机器上使用这些计算机,所以更改有点困难 OS 和框架)。
无论如何,这是我用来捕获屏幕的方法,然后是我用来更改像素颜色深度的实际方法。
int DefaultMaxScreenDiskSize = 212500;
private bool StreamScreen()
{
Rectangle bounds = Screen.PrimaryScreen.Bounds;
IntPtr hdc = GetDC(IntPtr.Zero);
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format16bppRgb555);
using (MemoryStream ms = new MemoryStream())
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
IntPtr dstHdc = graphics.GetHdc();
BitBlt(dstHdc, 0, 0, bounds.Width, bounds.Height, hdc, 0, 0, Enums.RasterOperation.SRC_COPY);
graphics.ReleaseHdc(dstHdc);
}
bitmap.Save(ms, ImageFormat.Png);
int length = ms.ToArray().Length;
if (length > DefaultMaxScreenDiskSize)
{
Bitmap nBitmap = ApplyDecreaseColourDepth(128, bitmap);
nBitmap.Save(ms, ImageFormat.Png);
}
using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
currentImgHash = Convert.ToBase64String(sha1.ComputeHash(ms.ToArray()));
if (oldImgHash != currentImgHash || ForceScreenRefresh)
{
ReleaseDC(IntPtr.Zero, hdc);
if (ms.ToArray() != null)
{
while (!SendScreen(ZlibStream.CompressBuffer(ms.ToArray()))) ;
oldImgHash = currentImgHash;
ForceScreenRefresh = false;
return true;
}
}
}
return false;
}
这是我使用的另一种方法,即改变像素深度的方法。但是,它有点慢,而且对系统来说很重,所以我不知道如何改变它。
public Bitmap ApplyDecreaseColourDepth(int offset, Bitmap bitmapImage)
{
for (int y = 0; y < bitmapImage.Height; y++)
{
for (int x = 0; x < bitmapImage.Width; x++)
{
Color pixelColor = bitmapImage.GetPixel(x, y);
int R = Math.Max(0, (pixelColor.R + offset / 2) / offset * offset - 1);
int G = Math.Max(0, (pixelColor.G + offset / 2) / offset * offset - 1);
int B = Math.Max(0, (pixelColor.B + offset / 2) / offset * offset - 1);
bitmapImage.SetPixel(x, y, Color.FromArgb(R, G, B));
}
}
return bitmapImage;
}
知道如何更改深度像素或压缩位图吗?我试图压缩图像,但(这是一个 .PNG)它只改变了几个字节(例如正常大小 ms.length = 38689,压缩它 ms.length = 38489)
很遗憾,我还没有解决压缩问题的方法。但我记得我前一段时间看到的一篇来自 CodeProject 的文章,它准确地描述了 GetPixel()
和 SetPixel()
非常慢的问题。这个限制可以通过下面的代码来克服。
为了使其编译,您需要在相应项目的构建选项中允许不安全代码。
public unsafe Bitmap ApplyDecreaseColourDepth(int offset, Bitmap bitmapImage)
{
BitmapData data = bitmapImage.LockBits(
new Rectangle(0, 0, bitmapImage.Width, bitmapImage.Height),
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
const int bytesPerPixel = 3;
Byte* scan0 = (Byte*)data.Scan0.ToPointer();
Int32 stride = data.Stride;
for (int y = 0; y < bitmapImage.Height; y++)
{
Byte* row = scan0 + (y * stride);
for (int x = 0; x < bitmapImage.Width; x++)
{
Int32 bIndex = x * bytesPerPixel;
Int32 gIndex = bIndex + 1;
Int32 rIndex = bIndex + 2;
Byte pixelR = row[rIndex];
Byte pixelG = row[gIndex];
Byte pixelB = row[bIndex];
row[rIndex] = (Byte)Math.Max(0, (pixelR + offset / 2) / offset * offset - 1);
row[gIndex] = (Byte)Math.Max(0, (pixelG + offset / 2) / offset * offset - 1);
row[bIndex] = (Byte)Math.Max(0, (pixelB + offset / 2) / offset * offset - 1);
}
}
bitmapImage.UnlockBits(data);
return bitmapImage;
}
这是未经测试的,所以请在将其投入生产之前进行测试!可以在 https://www.codeproject.com/Articles/617613/Fast-pixel-operations-in-NET-with-and-without-unsa
下找到我正在谈论的文章(以及我曾经更改过您的代码的文章)