c# - 在 C# 中使用多线程屏蔽/过滤图像(Windows 窗体应用程序)
问题描述
我目前正在尝试学习如何在 C# 中使用多线程,并且我真的很难让用户从 UI 中选择要使用的线程数。
在一些来源的帮助下,我编写了代码来使用中值滤波器屏蔽图像,现在想要应用多线程 (1-64)。
我知道线程是如何工作的,但我正在努力寻找在这个程序中实现它的方法。
有人可以通过不向我展示所需的代码来帮助我理解,而是解释应该在哪里实现线程以及如何允许用户选择线程数量。
代码
ExtBitmap 类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing.Imaging;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
namespace EilandFotoPRAK1
{
public static class ExtBitmap
{
public static Bitmap CopyToSquareCanvas(this Bitmap sourceBitmap, int canvasWidthLenght)
{
float ratio = 1.0f;
int maxSide = sourceBitmap.Width > sourceBitmap.Height ?
sourceBitmap.Width : sourceBitmap.Height;
ratio = (float)maxSide / (float)canvasWidthLenght;
Bitmap bitmapResult = (sourceBitmap.Width > sourceBitmap.Height ?
new Bitmap(canvasWidthLenght, (int)(sourceBitmap.Height / ratio))
: new Bitmap((int)(sourceBitmap.Width / ratio), canvasWidthLenght));
using (Graphics graphicsResult = Graphics.FromImage(bitmapResult))
{
graphicsResult.CompositingQuality = CompositingQuality.HighQuality;
graphicsResult.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphicsResult.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphicsResult.DrawImage(sourceBitmap,
new Rectangle(0, 0,
bitmapResult.Width, bitmapResult.Height),
new Rectangle(0, 0,
sourceBitmap.Width, sourceBitmap.Height),
GraphicsUnit.Pixel);
graphicsResult.Flush();
}
return bitmapResult;
}
public static Bitmap MedianFilter(this Bitmap sourceBitmap,
int matrixSize,
int bias = 0,
bool grayscale = false)
{
BitmapData sourceData =
sourceBitmap.LockBits(new Rectangle(0, 0,
sourceBitmap.Width, sourceBitmap.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
byte[] pixelBuffer = new byte[sourceData.Stride *
sourceData.Height];
byte[] resultBuffer = new byte[sourceData.Stride *
sourceData.Height];
Marshal.Copy(sourceData.Scan0, pixelBuffer, 0,
pixelBuffer.Length);
sourceBitmap.UnlockBits(sourceData);
if (grayscale == true)
{
float rgb = 0;
for (int k = 0; k < pixelBuffer.Length; k += 4)
{
rgb = pixelBuffer[k] * 0.11f;
rgb += pixelBuffer[k + 1] * 0.59f;
rgb += pixelBuffer[k + 2] * 0.3f;
pixelBuffer[k] = (byte)rgb;
pixelBuffer[k + 1] = pixelBuffer[k];
pixelBuffer[k + 2] = pixelBuffer[k];
pixelBuffer[k + 3] = 255;
}
}
int filterOffset = (matrixSize - 1) / 2;
int calcOffset = 0;
int byteOffset = 0;
List<int> neighbourPixels = new List<int>();
byte[] middlePixel;
for (int offsetY = filterOffset; offsetY <
sourceBitmap.Height - filterOffset; offsetY++)
{
for (int offsetX = filterOffset; offsetX <
sourceBitmap.Width - filterOffset; offsetX++)
{
byteOffset = offsetY *
sourceData.Stride +
offsetX * 4;
neighbourPixels.Clear();
for (int filterY = -filterOffset;
filterY <= filterOffset; filterY++)
{
for (int filterX = -filterOffset;
filterX <= filterOffset; filterX++)
{
calcOffset = byteOffset +
(filterX * 4) +
(filterY * sourceData.Stride);
neighbourPixels.Add(BitConverter.ToInt32(
pixelBuffer, calcOffset));
}
}
neighbourPixels.Sort();
middlePixel = BitConverter.GetBytes(
neighbourPixels[filterOffset]);
resultBuffer[byteOffset] = middlePixel[0];
resultBuffer[byteOffset + 1] = middlePixel[1];
resultBuffer[byteOffset + 2] = middlePixel[2];
resultBuffer[byteOffset + 3] = middlePixel[3];
}
}
Bitmap resultBitmap = new Bitmap(sourceBitmap.Width,
sourceBitmap.Height);
BitmapData resultData =
resultBitmap.LockBits(new Rectangle(0, 0,
resultBitmap.Width, resultBitmap.Height),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb);
Marshal.Copy(resultBuffer, 0, resultData.Scan0,
resultBuffer.Length);
resultBitmap.UnlockBits(resultData);
return resultBitmap;
}
}
}
FrmPhoto 类 --------------------------------------- GUI 的外观
public partial class FrmPhoto : Form
{
private Bitmap originalBitmap = null;
private Bitmap previewBitmap = null;
private Bitmap resultBitmap = null;
public FrmPhoto()
{
InitializeComponent();
cmbEdge.SelectedIndex = 0;
}
private void btnOpen_Click(object sender, EventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Title = "Select an image file.";
dialog.Filter = "Png Images(*.png)|*.png|Jpeg Images(*.jpg)|*.jpg";
dialog.Filter += "|Bitmap Images(*.bmp)|*.bmp";
if (dialog.ShowDialog() == DialogResult.OK)
{
StreamReader streamReader = new StreamReader(dialog.FileName);
originalBitmap = (Bitmap)Bitmap.FromStream(streamReader.BaseStream);
streamReader.Close();
previewBitmap = originalBitmap.CopyToSquareCanvas(pix1.Width);
pix1.Image = previewBitmap;
}
}
private void ApplyFilter(bool preview)
{
if (previewBitmap == null || cmbEdge.SelectedIndex == -1)
{
return;
}
Bitmap selectedSource = null;
Bitmap bitmapResult = null;
if (preview == true)
{
selectedSource = previewBitmap;
}
else
{
selectedSource = originalBitmap;
}
if (selectedSource != null)
{
if (cmbEdge.SelectedItem.ToString() == "None")
{
bitmapResult = selectedSource;
}
else if (cmbEdge.SelectedItem.ToString() == "Median 3x3")
{
bitmapResult = selectedSource.MedianFilter(3);
}
else if (cmbEdge.SelectedItem.ToString() == "Median 5x5")
{
bitmapResult = selectedSource.MedianFilter(5);
}
else if (cmbEdge.SelectedItem.ToString() == "Median 7x7")
{
bitmapResult = selectedSource.MedianFilter(7);
}
else if (cmbEdge.SelectedItem.ToString() == "Median 9x9")
{
bitmapResult = selectedSource.MedianFilter(9);
}
else if (cmbEdge.SelectedItem.ToString() == "Median 11x11")
{
bitmapResult = selectedSource.MedianFilter(11);
}
else if (cmbEdge.SelectedItem.ToString() == "Median 13x13")
{
bitmapResult = selectedSource.MedianFilter(13);
}
}
if (bitmapResult != null)
{
if (preview == true)
{
pix1.Image = bitmapResult;
}
else
{
resultBitmap = bitmapResult;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
ApplyFilter(true);
}
}
解决方案
我们可以使用Parallel.For方法来为我们进行并行化。
我们必须使在并行循环内修改的所有变量都是本地的。
int filterOffset = (matrixSize - 1) / 2;
//int calcOffset = 0;
//int byteOffset = 0;
//List<int> neighbourPixels = new List<int>();
//byte[] middlePixel;
var w = sourceBitmap.Width - filterOffset;
for (int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++)
{
Parallel.For(filterOffset, w, offsetX =>
{
var byteOffset = offsetY * sourceData.Stride + offsetX * 4; // local
var neighbourPixels = new List<int>(); // local
//neighbourPixels.Clear();
for (int filterY = -filterOffset; filterY <= filterOffset; filterY++)
{
for (int filterX = -filterOffset; filterX <= filterOffset; filterX++)
{
var calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride); // local
neighbourPixels.Add(BitConverter.ToInt32(pixelBuffer, calcOffset));
}
}
neighbourPixels.Sort();
var middlePixel = BitConverter.GetBytes(neighbourPixels[filterOffset]); // local
resultBuffer[byteOffset] = middlePixel[0];
resultBuffer[byteOffset + 1] = middlePixel[1];
resultBuffer[byteOffset + 2] = middlePixel[2];
resultBuffer[byteOffset + 3] = middlePixel[3];
});
};
这使我的机器的性能提高了两倍以上(2 个物理内核,4 个逻辑内核)。
外循环的并行化肯定会提供更多的加速。但是,让它变得更加困难。
以类似的方式,可以对灰度滤波器进行并行化。
推荐阅读
- node.js - 如何在单个快速路由 User.route('/').get((req,res,next)=>{ }) 上添加两个身份验证?
- python - 如果它们部分满足某些条件,如何从数据框中删除某些行
- r - 使用 geom_errorbarh 在 ggplot 的 x.axis 上仅显示最小值和最大值
- c# - 如何在控制台应用程序中将执行从工作线程切换到主线程?
- c++ - C ++ MySQL连接器执行存储过程损坏连接
- python - Python。递归矩阵乘积,结果为一组向量
- javascript - rails 6在提交后保留选定的输入
- c++ - 关于visual studio中第三方库(oat++)配置的问题
- vert.x - 为每个 HTTP 请求部署新的 Verticle?
- rabbitmq - 如何找到绑定到部署在 Tanzu (Pivotal/PCF) 环境中的 SCDF 流的 RabbitMQ 服务器的连接信息?