📄 effect.extend.cs
字号:
b.UnlockBits(srcData);
dstImage.UnlockBits(dstData);
b.Dispose();
return dstImage;
} // end of Mosaic
/// <summary>
/// 油画
/// </summary>
/// <param name="b">位图流</param>
/// <param name="brushSize">画刷尺寸[1, 8]</param>
/// <param name="coarseness">粗糙度[1, 255]</param>
/// <returns></returns>
public Bitmap OilPainting(Bitmap b, int brushSize, byte coarseness)
{
if (brushSize < 1) brushSize = 1;
if (brushSize > 8) brushSize = 8;
if (coarseness < 1) coarseness = 1;
if (coarseness > 255) coarseness = 255;
int width = b.Width;
int height = b.Height;
int lenArray = coarseness + 1;
int[] CountIntensity = new int[lenArray];
uint[] RedAverage = new uint[lenArray];
uint[] GreenAverage = new uint[lenArray];
uint[] BlueAverage = new uint[lenArray];
uint[] AlphaAverage = new uint[lenArray];
// 产生灰度数组
GrayProcessing gp = new GrayProcessing();
Bitmap grayImage = gp.Gray( (Bitmap)b.Clone(), GrayProcessing.GrayMethod.WeightAveraging );
byte[,] Gray = gp.Image2Array(grayImage);
grayImage.Dispose();
// 目标图像
Bitmap dstImage = new Bitmap(width, height);
BitmapData srcData = b.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData dstData = dstImage.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
int stride = srcData.Stride;
System.IntPtr srcScan0 = srcData.Scan0;
System.IntPtr dstScan0 = dstData.Scan0;
int offset = stride - width * BPP;
unsafe
{
byte* src = (byte*)srcScan0;
byte* dst = (byte*)dstScan0;
for (int y = 0; y < height; y++)
{
// 油画渲染范围上下边界
int top = y - brushSize;
int bottom = y + brushSize + 1;
if (top < 0) top = 0;
if (bottom >= height) bottom = height - 1;
for (int x = 0; x < width; x++)
{
// 油画渲染范围左右边界
int left = x - brushSize;
int right = x + brushSize + 1;
if (left < 0) left = 0;
if (right >= width) right = width - 1;
// 初始化数组
for (int i = 0; i < lenArray; i++)
{
CountIntensity[i] = 0;
RedAverage[i] = 0;
GreenAverage[i] = 0;
BlueAverage[i] = 0;
AlphaAverage[i] = 0;
} // i
// 下面这个内循环类似于外面的大循环
// 也是油画特效处理的关键部分
src = (byte*)srcScan0 + top * stride + left * BPP;
int offsetBlock = stride - (right - left) * BPP;
for (int j = top; j < bottom; j++)
{
for (int i = left; i < right; i++)
{
byte intensity = (byte)(coarseness * Gray[i, j] / 255);
CountIntensity[intensity]++;
AlphaAverage[intensity] += src[3];
RedAverage[intensity] += src[2];
GreenAverage[intensity] += src[1];
BlueAverage[intensity] += src[0];
src += BPP;
} // i
src += offsetBlock;
} // j
// 求最大值,并记录下数组索引
byte chosenIntensity = 0;
int maxInstance = CountIntensity[0];
for (int i = 1; i < lenArray; i++)
{
if (CountIntensity[i] > maxInstance)
{
chosenIntensity = (byte)i;
maxInstance = CountIntensity[i];
}
} // i
dst[3] = (byte)(AlphaAverage[chosenIntensity] / maxInstance);
dst[2] = (byte)(RedAverage[chosenIntensity] / maxInstance);
dst[1] = (byte)(GreenAverage[chosenIntensity] / maxInstance);
dst[0] = (byte)(BlueAverage[chosenIntensity] / maxInstance);
dst += BPP;
} // x
dst += offset;
} // y
}
b.UnlockBits(srcData);
dstImage.UnlockBits(dstData);
b.Dispose();
return dstImage;
} // end of OilPainting
/// <summary>
/// 曝光
/// </summary>
/// <param name="b">位图流</param>
/// <returns></returns>
public Bitmap Solarize(Bitmap b)
{
int width = b.Width;
int height = b.Height;
BitmapData data = b.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
byte* p = (byte*)data.Scan0;
int offset = data.Stride - width * BPP;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (p[0] < 128) p[0] = (byte)(p[0] ^ 0xFF); // B
if (p[1] < 128) p[1] = (byte)(p[1] ^ 0xFF); // G
if (p[2] < 128) p[2] = (byte)(p[2] ^ 0xFF); // R
p += BPP;
}// x
p += offset;
} // y
}
b.UnlockBits(data);
return b;
} // end of Solarize
/************************************************************
*
* 图像融合
*
************************************************************/
/// <summary>
/// 图像融合
/// </summary>
/// <param name="bgImage">背景图像</param>
/// <param name="fgImage">前景图像</param>
/// <param name="transparency">前景透明度[0, 255]</param>
/// <returns></returns>
public Bitmap Inosculate(Bitmap bgImage, Bitmap fgImage, byte transparency)
{
int width = bgImage.Width;
int height = bgImage.Height;
BitmapData bgData = bgImage.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
BitmapData fgData = fgImage.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
unsafe
{
byte* bg = (byte*)bgData.Scan0;
byte* fg = (byte*)fgData.Scan0;
int offset = bgData.Stride - width * BPP;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (fg[3] != 0)
{
// pixel = FG * transparency / 255 + BG * (255 - transparency) / 255
bg[0] = (byte)((fg[0] - bg[0]) * transparency / 255 + bg[0]);
bg[1] = (byte)((fg[1] - bg[1]) * transparency / 255 + bg[1]);
bg[2] = (byte)((fg[2] - bg[2]) * transparency / 255 + bg[2]);
}
bg += BPP;
fg += BPP;
} // x
bg += offset;
fg += offset;
} // y
}
bgImage.UnlockBits(bgData);
fgImage.UnlockBits(fgData);
Bitmap dstImage = (Bitmap)bgImage.Clone();
bgImage.Dispose();
fgImage.Dispose();
return dstImage;
} // end of Inosculate
/// <summary>
/// 将两张图像合成为魔术图
/// </summary>
/// <param name="bgImage">背景图像</param>
/// <param name="fgImage">前景图像</param>
/// <param name="transparency">透明度[0, 255],即隐藏状况</param>
/// <param name="contrast">前景图与背景图的对比度[-100, 100]</param>
public Bitmap Magic(Bitmap bgImage, Bitmap fgImage, byte transparency, int contrast)
{
Adjustment a = new Adjustment();
Effect e = new Effect();
// 按像素交错翻转
bgImage = a.Interleaving(bgImage);
// 处理对比度
bgImage = a.Contrast(bgImage, contrast);
// 前景和背景进行混合处理
Bitmap dstImage = e.Inosculate(bgImage, fgImage, transparency);
bgImage.Dispose();
fgImage.Dispose();
return dstImage;
} // end of Magic
/************************************************************
*
* 自定义、去红眼、艺术字符
*
************************************************************/
/// <summary>
/// 自定义
/// </summary>
/// <param name="b">位图流</param>
/// <param name="sequence">卷积核元素序列</param>
/// <returns></returns>
public Bitmap Custom(Bitmap b, int[] sequence)
{
MatrixNxN m = new MatrixNxN();
m.Sequence = sequence;
return m.Convolute(b);
} // end of Custom
/// <summary>
/// 自定义
/// </summary>
/// <param name="b">位图流</param>
/// <param name="kernel">卷积核</param>
/// <param name="scale">缩放比例</param>
/// <param name="offset">偏移量</param>
/// <returns></returns>
public Bitmap Custom(Bitmap b, int[,] kernel, int scale, int offset)
{
MatrixNxN m = new MatrixNxN();
m.Kernel = kernel;
m.Scale = scale;
m.Offset = offset;
return m.Convolute(b);
} // end of Custom
/// <summary>
/// 去除红眼
/// </summary>
/// <param name="b">位图流</param>
/// <param name="tolerence">容差[0, 100]</param>
/// <param name="red">红色分量[0, 255]</param>
/// <param name="green">绿色分量[0, 255]</param>
/// <param name="blue">蓝色分量[0, 255]</param>
/// <returns></returns>
public Bitmap RedEyeRemoval(Bitmap b, int tolerence, int red, int green, int blue)
{
if (tolerence < 0) tolerence = 0;
if (tolerence > 100) tolerence = 100;
if (red < 0) red = 0;
if (red > 255) red = 255;
if (green < 0) green = 0;
if (green > 255) green = 255;
if (blue < 0) blue = 0;
if (blue > 255) blue = 255;
int width = b.Width;
int height = b.Height;
BitmapData data = b.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
byte* p = (byte*)data.Scan0;
int offset = data.Stride - width * BPP;
byte R, G, B;
int gray;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
R = p[2];
G = p[1];
B = p[0];
// R 分量与 G, B 两分量差距越大,则红色成分越多
int difference = R - (G > B ? G : B);
// 如果当前像素颜色分量差距在指定容差范围内,并且红色成份足够高
// 则认为当前像素需要进行红眼去除处理
if ((difference > tolerence) && (R > 100))
{
// 求灰度
gray = (19661 * R + 38666 * G + 7209 * B) >> 16;
// 用指定的颜色替换掉红色
p[2] = (byte)(gray * red / 255);
p[1] = (byte)(gray * green / 255);
p[0] = (byte)(gray * blue / 255);
}
p += BPP;
} // x
p += offset;
} // y
}
b.UnlockBits(data);
return b;
} // end of RedEyeRemoval
/// <summary>
/// 艺术文字,将图片转换为字符
/// </summary>
/// <param name="b">位图流</param>
/// <param name="text">待显示的文字</param>
/// <param name="blockWidth">块宽</param>
/// <param name="blockHeight">块高</param>
/// <returns></returns>
public string Image2Text(Bitmap b, string text, int blockWidth, int blockHeight)
{
int width = b.Width;
int height = b.Height;
// 首先将图像灰度化
GrayProcessing gp = new GrayProcessing();
b = gp.Gray(b, GrayProcessing.GrayMethod.WeightAveraging);
byte[,] Gray = gp.Image2Array(b);
// 用于显示的灰度字符
char[] ArtChar = text.ToCharArray();
int len = ArtChar.Length;
// 统计每个字符的点数
int[] CharDots = new int[len];
Watermark w = new Watermark();
for (int i = 0; i < len; i++)
{
CharDots[i] = w.CountTextDots(ArtChar[i]);
} // i
// 对字符点数进行冒泡法排序
for (int i = 0; i < len - 1; i++)
{
for (int j = i + 1; j < len; j++)
{
if (CharDots[j] < CharDots[i])
{
// 交换点数
int t = CharDots[j];
CharDots[j] = CharDots[i];
CharDots[i] = t;
// 交换字符
char c = ArtChar[j];
ArtChar[j] = ArtChar[i];
ArtChar[i] = c;
}
} // j
} // i
// 最大点数
int maxGray = CharDots[len - 1];
// 累加块灰度
int sum = 0;
// 块积
double product = maxGray / (double)(blockWidth * blockHeight * 255);
// 图案化后的字符串,即返回字符串
string artString = "";
for (int y = 0; y < height; y += blockHeight)
{
for (int x = 0; x < width; x += blockWidth)
{
sum = 0;
for (int yB = 0; yB < blockHeight && (y + yB) < height; yB++)
{
for (int xB = 0; xB < blockWidth && (x + xB) < width; xB++)
{
// 累加小块内的灰度
sum += Gray[x + xB, y + yB];
} // xB
} // yB
// gray = maxGray * (sum / (blockWidth * blockHeight)) / 255
int gray = (int)(sum * product);
// 寻找灰度最接近的字符
int k = 0;
while (CharDots[k] < (maxGray - gray))
k++;
// 进行灰度映射
artString += ArtChar[k];
} // x
artString += "\r\n";
} // y
return artString;
} // end of Image2Text
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -