- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我已经编写了一些例程来使用 3x3 内核锐化灰度图像,
-1 -1 -1
-1 9 -1
-1 -1 -1
以下代码在非 FFT(空间域)卷积的情况下运行良好,但不适用于基于 FFT(频域)的卷积。
输出的图像似乎很模糊。
我有几个问题:
(1) 此例程无法生成所需的结果。它还会卡住应用程序。
public static Bitmap ApplyWithPadding(Bitmap image, Bitmap mask)
{
if(image.PixelFormat == PixelFormat.Format8bppIndexed)
{
Bitmap imageClone = (Bitmap)image.Clone();
Bitmap maskClone = (Bitmap)mask.Clone();
/////////////////////////////////////////////////////////////////
Complex[,] cPaddedLena = ImageDataConverter.ToComplex(imageClone);
Complex[,] cPaddedMask = ImageDataConverter.ToComplex(maskClone);
Complex[,] cConvolved = Convolution.Convolve(cPaddedLena, cPaddedMask);
return ImageDataConverter.ToBitmap(cConvolved);
}
else
{
throw new Exception("not a grascale");
}
}
(2) 这个程序给出了很好的结果。但是,慢得要命。
public static Bitmap Apply(Bitmap sourceBitmap)
{
Sharpen filter = new Sharpen();
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);
double blue = 0.0;
double green = 0.0;
double red = 0.0;
int filterWidth = filter.FilterMatrix.GetLength(1);
int filterHeight = filter.FilterMatrix.GetLength(0);
int filterOffset = (filterWidth - 1) / 2;
int calcOffset = 0;
int byteOffset = 0;
for (int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++)
{
for (int offsetX = filterOffset; offsetX <
sourceBitmap.Width - filterOffset; offsetX++)
{
blue = 0;
green = 0;
red = 0;
byteOffset = offsetY *
sourceData.Stride +
offsetX * 4;
for (int filterY = -filterOffset;
filterY <= filterOffset; filterY++)
{
for (int filterX = -filterOffset;
filterX <= filterOffset; filterX++)
{
calcOffset = byteOffset +
(filterX * 4) +
(filterY * sourceData.Stride);
blue += (double)(pixelBuffer[calcOffset]) *
filter.FilterMatrix[filterY + filterOffset,
filterX + filterOffset];
green += (double)(pixelBuffer[calcOffset + 1]) *
filter.FilterMatrix[filterY + filterOffset,
filterX + filterOffset];
red += (double)(pixelBuffer[calcOffset + 2]) *
filter.FilterMatrix[filterY + filterOffset,
filterX + filterOffset];
}
}
blue = filter.Factor * blue + filter.Bias;
green = filter.Factor * green + filter.Bias;
red = filter.Factor * red + filter.Bias;
if (blue > 255)
{ blue = 255; }
else if (blue < 0)
{ blue = 0; }
if (green > 255)
{ green = 255; }
else if (green < 0)
{ green = 0; }
if (red > 255)
{ red = 255; }
else if (red < 0)
{ red = 0; }
resultBuffer[byteOffset] = (byte)(blue);
resultBuffer[byteOffset + 1] = (byte)(green);
resultBuffer[byteOffset + 2] = (byte)(red);
resultBuffer[byteOffset + 3] = 255;
}
}
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;
}
(3) 下面是我的GUI代码。如果我使用图像作为 mask ,SharpenFilter.ApplyWithPadding()
可以正常工作。但是,如果我使用 3
x3
内核,则不起作用。
string path = @"E:\lena.png";
string path2 = @"E:\mask.png";
Bitmap _inputImage;
Bitmap _maskImage;
private void LoadImages_Click(object sender, EventArgs e)
{
_inputImage = Grayscale.ToGrayscale(Bitmap.FromFile(path) as Bitmap);
/*
_maskImage = Grayscale.ToGrayscale(Bitmap.FromFile(path2) as Bitmap);
*/
SharpenFilter filter = new SharpenFilter();
double[,] mask = new double[,] { { -1, -1, -1, },
{ -1, 9, -1, },
{ -1, -1, -1, }, };
_maskImage = ImageDataConverter.ToBitmap(mask);
inputImagePictureBox.Image = _inputImage;
maskPictureBox.Image = _maskImage;
}
Bitmap _paddedImage;
Bitmap _paddedMask;
private void padButton_Click(object sender, EventArgs e)
{
Bitmap lena = Grayscale.ToGrayscale(_inputImage);
Bitmap mask = Grayscale.ToGrayscale(_maskImage);
////Not working...
//int maxWidth = (int)Math.Max(lena.Width, mask.Width);
//int maxHeight = (int)Math.Max(lena.Height, mask.Height);
////This is working correctly in case if I use a png image as a mask.
int maxWidth = (int)Tools.ToNextPow2(Convert.ToUInt32(lena.Width + mask.Width));
int maxHeight = (int)Tools.ToNextPow2(Convert.ToUInt32(lena.Height + mask.Height));
_paddedImage = ImagePadder.Pad(lena, maxWidth, maxHeight);
_paddedMask = ImagePadder.Pad(mask, maxWidth, maxHeight);
paddedImagePictureBox.Image = _paddedImage;
paddedMaskPictureBox.Image = _paddedMask;
}
private void filterButton_Click(object sender, EventArgs e)
{
// Not working properly.
// Freezes the application.
Bitmap sharp = SharpenFilter.ApplyWithPadding(_paddedImage, _paddedMask);
////Works well. But, very slow.
//Bitmap sharp = SharpenFilter.Apply(_paddedImage);
filteredPictureBox.Image = sharp as Bitmap;
}
输出:
源代码:
最佳答案
主要问题似乎在于将内核解释为由无符号字节值组成的图像。结果,-1
值被转换为 255
,有效地计算了与内核的卷积
255 255 255
255 9 255
255 255 255
这可以从“卷积核”图像中的白色区域立即观察到。因此,生成的内核是低通滤波器的内核,产生相应的模糊效果。
处理这个问题的最佳方法可能是将内核作为带符号值的矩阵而不是图像来读取。
如果您仍然喜欢将内核作为图像处理,则需要将图像转换回有符号值。我能想到的实现此结果的最简单方法是创建 ImageDataConverter.ToInteger(Bitmap)
的修改版本,您可以在其中将字节映射到有符号值:
public static Complex[,] Unwrap(Bitmap bitmap)
{
int Width = bitmap.Width;
int Height = bitmap.Height;
Complex[,] array2D = new Complex[bitmap.Width, bitmap.Height];
...
else// If there is only one channel:
{
iii = (int)(*address);
if (iii >= 128)
{
iii -= 256;
}
}
Complex tempComp = new Complex((double)iii, 0.0);
array2D[x, y] = tempComp;
然后您可以使用以下方法在 SharpenFilter.ApplyWithPadding
中转换您的图像:
Complex[,] cPaddedMask = ImageDataConverter.Unwrap(maskClone);
这应该会给您以下结果:
虽然这提高了图像的清晰度,但您应该立即注意到图像比原始图像暗得多。这是由于 Convolution.Rescale
函数根据图像的最小值和最大值动态重新缩放图像。这可以方便地显示具有最大动态范围的图像,但可能会导致与标准卷积不同的整体缩放比例。要实现此标准缩放(基于 FFT 实现的缩放),您可以使用以下实现:
//Rescale values between 0 and 255.
private static void Rescale(Complex[,] convolve)
{
int imageWidth = convolve.GetLength(0);
int imageHeight = convolve.GetLength(1);
double scale = imageWidth * imageHeight;
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
double re = Math.Max(0, Math.Min(convolve[i, j].Real * scale, 255.0));
double im = Math.Max(0, Math.Min(convolve[i, j].Imaginary * scale, 255.0));
convolve[i, j] = new Complex(re, im);
}
}
}
这应该会为您提供亮度级别更合适的图像:
最后,对于过滤操作,人们通常会期望结果与原始图像大小相匹配(不同于包含尾部的卷积)。在 SharpenFilter.ApplyWithPadding
中裁剪结果:
...
// -3 terms are due to kernel size
// +5 vertical offset term is due to vertical reflection & offset in SetPixel
Rectangle rect = new Rectangle((cPaddedLena.GetLength(0) / 2 - 3) / 2,
(cPaddedLena.GetLength(1) / 2 - 3) / 2 + 5,
cPaddedLena.GetLength(0) / 2,
cPaddedLena.GetLength(1) / 2);
return ImageDataConverter.ToBitmap(cConvolved).Clone(rect, PixelFormat.Format8bppIndexed);
应该给你:
为了便于视觉对比,这里还是原图:
关于c# - FFT 卷积 - 3x3 内核,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39114265/
总的来说,我对 Linux 内核和操作系统非常感兴趣。我想知道的是,内核的文件类型或扩展名是什么?它显然没有 .exe 或 .out 扩展名,因为它们用于安装在操作系统上的应用程序。 内核只是一个二进
我需要为 Raspbian Linux 内核添加一个自己的系统调用。现在我在搜索了大约 2 天以找到解决方案后陷入困境。 要加一个系统调用,我基本上是按照大纲来的( http://elinux.org
对于一个学术项目,我希望将源文件 (myfile.c) 添加到 kernel/目录,与exit.c相同的目录和 fork.c .构建系统似乎不会自动获取新文件,因为我在 myfile.c 中定义的函数
浏览器排行榜 浏览器市占率排行榜全球榜 。 浏览器市占率排行榜中国榜 -快科技 。 如果按照浏览器内核来看, Chromium 内核的市场占有率无疑是最大的,一家独大
给定一个进程或线程的任务结构,迭代属于同一进程的所有其他线程的习惯用法是什么? 最佳答案 Linux 不区分进程(任务)和线程。库调用 fork() 和 pthread_create() 使用相同的系
我正在用c(不是linux。完全从头开始)从头开始制作一个内核,但我遇到了一些问题。我有这个代码: #include "timer.h" int ms = 0; void timer_handler(
我正在从头开始制作一个 C 内核,我实际上只是从网站上复制了这段代码,因为我的代码无法工作,所以我很困惑。 void kmain(void) { const char *str = "my f
我不确定,如果我完全理解上述差异,所以我想自己解释一下,你可以打断我,只要我有错:“内核是创建内核线程的初始代码段。内核线程是由内核管理的进程。用户线程是进程的一部分。如果你有一个单线程进程,那么整个
看一下struct file 定义from this code Linux 内核版本 2.6.18。 我正在尝试比较代码中的两个 struct file 变量,并确定它们是否指的是同一个文件。该结构中
我试图在 Linux 启动时使嵌入式设备中的 LED 闪烁。基本上,LED 闪烁表明 Linux 正在启动。为了使 LED 闪烁,我正在做以下事情 在 init/main.c 中创建了一个全局定时器(
我有一些在 FreeBSD 和 Linux 上运行的特定硬件。 我必须做一个用户空间应用程序,它将使用内核/用户空间应用程序之间的共享内存与驱动程序一起工作。我的应用程序对来自用户空间的共享内存进行忙
我在哪里可以找到 linux 内核中相应函数的解释,特别是对于 ICMPv4? 例如:icmp_reply、icmp_send等 感谢您的帮助。 最好的,阿里木 最佳答案 探索 Linux 内核中的
我在 Linux Kernel 3.4 上工作,我有以下代码: /* Proximity sensor calibration values */ unsigned int als_kadc;
我正在阅读“罗伯特·洛夫 (Robert Love) 撰写的 Linux 内核开发第 3 版”,以大致了解 Linux 内核的工作原理..(2.6.2.3) 我对等待队列的工作方式感到困惑,例如这段代
我之前也问过同样的问题,但是我的帖子不知为何被删除了。 无论如何,我正在尝试使用 C++ 并编写一个允许我直接访问内存并向其中写入内容的程序。我听说我需要对内核做一些事情,因为它是连接操作系统和应用程
在尝试了解 Ruby 执行方法时,我找到了这篇关于在 Ruby 中运行命令的五种方法的博文 http://mentalized.net/journal/2010/03/08/5_ways_to_run
是否有 Linux 发行版(Minix 除外)包含良好的源代码文档?或者,是否有一些好的文档来描述一般的 Linux 源代码? 我已经下载了内核源代码,但是(不出所料)我有点不知所措,我想知道是否有一
有谁知道 linux 中的哪个函数或文件包含查找用于 bind() 系统调用的随机端口的算法?我到处寻找,在 Linux 源代码中找不到包含此算法的方法。 谢谢! 最佳答案 这是一段又长又复杂的代码,
前言 首先,对于有科班背景的读者,可以跳过本系列文章。这些文章的主要目的是通过简单易懂的汇总,帮助非科班出身的读者理解底层知识,进一步了解为什么在面试中会涉及这些底层问题。否则,某些概念将始终
CentOS7.2与CentOS6区别及特点 Linux 操作系统的启动首先从 BIOS 开始,接下来进入 boot loader,由 bootloader 载入内核,进行内核初始化。内核初始化的
我是一名优秀的程序员,十分优秀!