[英]What's a good pixelation algorithm in C# .NET?
在C#.NET中对图像进行像素化的好算法是什么?
一个简单而又无效的解决方案是调整大小,然后使用像素复制调整大小。
更好的解决方案是(伪代码):
(时间O(n),附加空间(除了可变源图像):O(1))
// Pixelize in x axis (choose a whole k s.t. 1 <= k <= Width)
var sum = Pixel[0, 0];
for (y = 0; y < Height; y++)
{
for (x = 0; x < Width + 1; x++)
{
if (x % k == 0)
{
sum /= k;
for (xl = Max(0, x-k); xl < x; xl++)
Pixel[y, xl] = sum;
sum = 0;
}
if (x == Width)
break;
sum += Pixel[y, x];
}
}
// Now do the same in the y axis
// (make sure to keep y the outer loop - for better performance)
// If your image has more than one channel, then then Pixel should be a struct.
在这个论坛上的人有一个非常好的算法。 它的工作原理是取每个“块”中所有颜色的平均值。
我今天刚刚在C#/ GDI +中使用了他的实现:
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Linq;
using System.Text;
/// <summary>
/// Applies a pixelation effect to an image.
/// </summary>
[SuppressMessage(
"Microsoft.Naming",
"CA1704",
Justification = "'Pixelate' is a word in my book.")]
public class PixelateEffect : EffectBase
{
/// <summary>
/// Gets or sets the block size, in pixels.
/// </summary>
private int blockSize = 10;
/// <summary>
/// Gets or sets the block size, in pixels.
/// </summary>
public int BlockSize
{
get
{
return this.blockSize;
}
set
{
if (value <= 1)
{
throw new ArgumentOutOfRangeException("value");
}
this.blockSize = value;
}
}
/// <summary>
/// Applies the effect by rendering it onto the target bitmap.
/// </summary>
/// <param name="source">The source bitmap.</param>
/// <param name="target">The target bitmap.</param>
public override void DrawImage(Bitmap source, Bitmap target)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (target == null)
{
throw new ArgumentNullException("target");
}
if (source.Size != target.Size)
{
throw new ArgumentException("The source bitmap and the target bitmap must be the same size.");
}
using (var graphics = Graphics.FromImage(target))
{
graphics.PageUnit = GraphicsUnit.Pixel;
for (int x = 0; x < source.Width; x += this.BlockSize)
{
for (int y = 0; y < source.Height; y += this.BlockSize)
{
var sums = new Sums();
for (int xx = 0; xx < this.BlockSize; ++xx)
{
for (int yy = 0; yy < this.BlockSize; ++yy)
{
if (x + xx >= source.Width || y + yy >= source.Height)
{
continue;
}
var color = source.GetPixel(x + xx, y + yy);
sums.A += color.A;
sums.R += color.R;
sums.G += color.G;
sums.B += color.B;
sums.T++;
}
}
var average = Color.FromArgb(
sums.A / sums.T,
sums.R / sums.T,
sums.G / sums.T,
sums.B / sums.T);
using (var brush = new SolidBrush(average))
{
graphics.FillRectangle(brush, x, y, (x + this.BlockSize), (y + this.BlockSize));
}
}
}
}
}
/// <summary>
/// A structure that holds sums for color averaging.
/// </summary>
private struct Sums
{
/// <summary>
/// Gets or sets the alpha component.
/// </summary>
public int A
{
get;
set;
}
/// <summary>
/// Gets or sets the red component.
/// </summary>
public int R
{
get;
set;
}
/// <summary>
/// Gets or sets the blue component.
/// </summary>
public int B
{
get;
set;
}
/// <summary>
/// Gets or sets the green component.
/// </summary>
public int G
{
get;
set;
}
/// <summary>
/// Gets or sets the total count.
/// </summary>
public int T
{
get;
set;
}
}
}
注意事项,在我的机器上工作,等等。
虽然我不知道一个众所周知的算法,但我确实要写类似的东西。 我使用的技术非常简单,但我认为对于大图像效率不高。 基本上我会拍摄图像并在5个(或者你想要的更大)像素块中进行颜色平均,然后使所有这些像素的颜色相同。 您可以通过仅对角线像素进行平均来加快速度,这样可以节省大量周期但不太准确。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.