- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
关于
我正在使用 WinForms
。在我的表单中,我有一个 picturebox
。 picturebox
大小模式设置为 zoom
。我使用 picturebox
查看 TIF 图像。 TIF 图像是灰度图像(仅限黑白)。
我的应用能做什么
我的应用程序找到文档中的第一个黑色像素和最后一个黑色像素,并在其周围绘制一个红色矩形。希望它会在图像内容周围绘制矩形。
问题
有时,TIF 文档在图像内容周围有 Blob /圆点。这使我的应用程序关闭。它不知道图像内容的开始和结束位置。如果文档有 Blob /点,如何找到 TIF 文档的内容并在其周围绘制一个矩形?
关于文档
下载测试图片链接:
• 文件滴管: http://www.filedropper.com/test-tifs
• 快速分享: https://ufile.io/2qiir
我发现了什么
根据我的研究,我发现了 AForge.Imaging 库,它有许多可能帮助我实现目标的成像过滤器。我正在考虑使用中值过滤器去除 Blob /点或使用其他过滤器来获得所需的结果。
我尝试了什么
我尝试应用 AForge 库中的中值过滤器来去除 Blob ,但只去除了一些 Blob 。我不得不多次重复回复过滤器以去除大部分位置以找到内容,但仍然很难找到内容。这种方法对我来说不太奏效。
AForge 过滤器链接: http://www.aforgenet.com/framework/docs/html/cdf93487-0659-e371-fed9-3b216efb6954.htm
代码
private void btn_Draw_Click(object sender, EventArgs e)
{
// Wrap the creation of the OpenFileDialog instance in a using statement,
// rather than manually calling the Dispose method to ensure proper disposal
using (OpenFileDialog dlg = new OpenFileDialog())
{
if (dlg.ShowDialog() == DialogResult.OK)
{
pictureBox1.Image = new Bitmap(dlg.FileName);
int xMax = pictureBox1.Image.Width;
int yMax = pictureBox1.Image.Height;
startX = Int32.MaxValue;
startY = Int32.MaxValue;
endX = Int32.MinValue;
endY = Int32.MinValue;
using (Bitmap bmp = new Bitmap(pictureBox1.Image))
{
for (var y = 0; y < yMax; y+=3)
{
for (var x = 0; x < xMax; x+=3)
{
Color col = bmp.GetPixel(x, y);
if(col.ToArgb() == Color.Black.ToArgb())
{
// Finds first black pixel
if (x < startX)
startX = x;
if(y < startY)
startY = y;
// Finds last black pixel
if (x > endX)
endX = x;
if (y > endY)
endY = y;
}
}
}
int picWidth = pictureBox1.Size.Width;
int picHeight = pictureBox1.Size.Height;
float imageRatio = xMax / (float)yMax; // image W:H ratio
float containerRatio = picWidth / (float)picHeight; // container W:H ratio
if (imageRatio >= containerRatio)
{
// horizontal image
float scaleFactor = picWidth / (float)xMax;
float scaledHeight = yMax * scaleFactor;
// calculate gap between top of container and top of image
float filler = Math.Abs(picHeight - scaledHeight) / 2;
//float filler = 0;
startX = (int)(startX * scaleFactor);
endX = (int)(endX * scaleFactor);
startY = (int)((startY) * scaleFactor + filler);
endY = (int)((endY) * scaleFactor + filler);
}
else
{
// vertical image
float scaleFactor = picHeight / (float)yMax;
float scaledWidth = xMax * scaleFactor;
float filler = Math.Abs(picWidth - scaledWidth) / 2;
startX = (int)((startX) * scaleFactor + filler);
endX = (int)((endX) * scaleFactor + filler);
startY = (int)(startY * scaleFactor);
endY = (int)(endY * scaleFactor);
}
//var scaleX = picWidth / (float)xMax;
//var scaleY = picHeight / (float)yMax;
//startX = (int)Math.Round(startX * scaleX);
//startY = (int)Math.Round(startY * scaleY);
//endX = (int)Math.Round(endX * scaleX);
//endY = (int)Math.Round(endY * scaleY);
}
}
}
}
private bool _once = true;
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (_once)
{
//Rectangle ee = new Rectangle(35, 183, 405, 157);
Rectangle ee = new Rectangle(startX, startY, endX - startX, endY - startY);
System.Diagnostics.Debug.WriteLine(startX + ", " + startY + ", " + (endX - startX) + ", " + (endY - startY));
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, ee);
}
//_once = false;
}
}
内容周围没有任何 Blob 的 Tif 文档
示例图片 1:
示例图片 2
示例图片 3
最佳答案
以下实验似乎满足您的所有要求。
我将以下控件放在 Form1 上
一个 MenuStrip:Docking=Top,有 2 个 MenuItems - 第一个用于打开文件,第二个用于运行算法
一个进度条:Docking=Top,用来观察加载和算法的性能
Docking=Fill 和 AutoScroll=true 的面板
一张图片放入面板,Point(0,0),其余默认。 SizeMode=正常。
更新
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// Opens an image file.
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
this.image = Image.FromFile(dlg.FileName) as Bitmap;
this.pictureBox1.Image = image;
this.pictureBox1.Invalidate();
}
}
Bitmap image;
// finds top, left, right and bottom bounds of the content in TIFF file.
//
private void findBoundsToolStripMenuItem_Click(object sender, EventArgs e)
{
int contentSize = 70;
this.left = 0;
this.top = 0;
this.right = this.pictureBox1.Width - 1;
this.bottom = this.pictureBox1.Height - 1;
int h = image.Height;
int w = image.Width;
this.progressBar1.Value = 0;
this.progressBar1.Maximum = 4;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
if (this.image.GetPixel(x, y).ToArgb() == Black)
{
int size = this.image.GetBlackRegionSize(x, y);
if (this.image.GetBlackRegionSize(x, y) > contentSize)
{
this.top = y;
goto label10;
}
}
}
}
label10:
this.progressBar1.Increment(1);
for (int y = h - 1; y >= 0; y--)
{
for (int x = 0; x < w; x++)
{
if (this.image.GetPixel(x, y).ToArgb() == Black)
{
if (this.image.GetBlackRegionSize(x, y) > contentSize)
{
this.bottom = y;
goto label11;
}
}
}
}
label11:
this.progressBar1.Increment(1);
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
if (this.image.GetPixel(x, y).ToArgb() == Black)
{
if (this.image.GetBlackRegionSize(x, y) > contentSize)
{
this.left = x;
goto label12;
}
}
}
}
label12:
this.progressBar1.Increment(1);
for (int x = w - 1; x >= 0; x--)
{
for (int y = 0; y < h; y++)
{
if (this.image.GetPixel(x, y).ToArgb() == Black)
{
if (this.image.GetBlackRegionSize(x, y) > contentSize)
{
this.right = x;
goto label13;
}
}
}
}
label13:
this.progressBar1.Increment(1);
this.pictureBox1.Invalidate();
}
internal static readonly int Black = Color.Black.ToArgb();
internal static readonly int White = Color.White.ToArgb();
int top;
int bottom;
int left;
int right;
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (pictureBox1.Image == null)
{
return;
}
int xMax = pictureBox1.Image.Width;
int yMax = pictureBox1.Image.Height;
int startX = this.left;
int startY = this.top;
int endX = this.right;
int endY = this.bottom;
int picWidth = pictureBox1.Size.Width;
int picHeight = pictureBox1.Size.Height;
float imageRatio = xMax / (float)yMax; // image W:H ratio
float containerRatio = picWidth / (float)picHeight; // container W:H ratio
if (imageRatio >= containerRatio)
{
// horizontal image
float scaleFactor = picWidth / (float)xMax;
float scaledHeight = yMax * scaleFactor;
// calculate gap between top of container and top of image
float filler = Math.Abs(picHeight - scaledHeight) / 2;
//float filler = 0;
startX = (int)(startX * scaleFactor);
endX = (int)(endX * scaleFactor);
startY = (int)((startY) * scaleFactor + filler);
endY = (int)((endY) * scaleFactor + filler);
}
else
{
// vertical image
float scaleFactor = picHeight / (float)yMax;
float scaledWidth = xMax * scaleFactor;
float filler = Math.Abs(picWidth - scaledWidth) / 2;
startX = (int)((startX) * scaleFactor + filler);
endX = (int)((endX) * scaleFactor + filler);
startY = (int)(startY * scaleFactor);
endY = (int)(endY * scaleFactor);
}
//if (_once)
//Rectangle ee = new Rectangle(35, 183, 405, 157);
Rectangle ee = new Rectangle(startX, startY, endX - startX, endY - startY);
System.Diagnostics.Debug.WriteLine(startX + ", " + startY + ", " + (endX - startX) + ", " + (endY - startY));
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, ee);
}
//_once = false;
}
}
static class BitmapHelper
{
internal static int GetBlackRegionSize(this Bitmap image, int x, int y)
{
int size = 0;
GetRegionSize(image, new List<Point>(), x, y, 0, ref size);
return size;
}
// this constant prevents StackOverFlow exception.
// also it has effect on performance.
// It's value must be greater than the value of contentSize defined in findBoundsToolStripMenuItem_Click(object sender, EventArgs e) method.
const int MAXLEVEL = 100;
static void GetRegionSize(this Bitmap image, List<Point> list, int x, int y, int level, ref int size)
{
if (x >= image.Width || x < 0 || y >= image.Height || y < 0 || list.Contains(x, y) || image.GetPixel(x, y).ToArgb() != Form1.Black || level > MAXLEVEL)
{
return;
}
if (size < level)
{
size = level;
}
list.Add(new Point(x, y));
image.GetRegionSize(list, x, y - 1, level + 1, ref size);
image.GetRegionSize(list, x, y + 1, level + 1, ref size);
image.GetRegionSize(list, x - 1, y, level + 1, ref size);
image.GetRegionSize(list, x + 1, y, level + 1, ref size);
}
static bool Contains(this List<Point> list, int x, int y)
{
foreach (Point point in list)
{
if (point.X == x && point.Y == y)
{
return true;
}
}
return false;
}
}
}
"this.pictureBox1.Size = image.Size;"已被删除。 Paint 事件处理程序的代码已更改。 PictureBox 大小模式现在可以设置为缩放。
更新 2
我试图简化代码并提高性能。
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.pictureBox1.Paint += new PaintEventHandler(this.pictureBox1_Paint);
}
// Opens an image file
// and runs "FindBounds()" method
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
this.image = Image.FromFile(dlg.FileName) as Bitmap;
FindBounds();
this.pictureBox1.Image = image;
this.pictureBox1.Invalidate();
}
}
Bitmap image;
// Possible maximum side of a spot or a dot in the image
int maxSpotOrDotSide = 7;
// Finds top, left, right and bottom bounds of the content in TIFF file.
private void FindBounds()
{
// Possible maximum area of a spot or a dot in the image
int maxSpotOrDotArea = maxSpotOrDotSide * maxSpotOrDotSide;
this.left = 0;
this.top = 0;
this.right = this.pictureBox1.Width - 1;
this.bottom = this.pictureBox1.Height - 1;
int h = image.Height;
int w = image.Width;
int num = w * h;
// Incrementers. I tested with greater values
// like "x = 2", "x = 5" and it increased performance.
// But we must be carefull as this may cause skipping content.
int dx = 1; // Incrementer for "x"
int dy = 1; // Incrementer for "y"
// Initialization of "progressBar1"
this.progressBar1.Value = 0;
this.progressBar1.Maximum = num;
// Content of the image
BlackContent imageContent = null;
// Here we will scan pixels of the image
// starting from top left corner and
// finishing at bottom right
for (int y = 0; y < h; y += dx)
{
for (int x = 0; x < w; x += dy)
{
int val = y * w + x;
this.progressBar1.Value = val > num ? num : val;
// This block skips scanning imageContent
// thus should increase performance.
if (imageContent != null && imageContent.Contains(x, y))
{
x = imageContent.Right;
continue;
}
// Interesting things begin to happen
// after we detect the first Black pixel
if (this.image.GetPixel(x, y).ToArgb() == Black)
{
BlackContent content = new BlackContent(x, y);
// Start Flood-Fill algorithm
content.FloodFill(this.image);
if (content.Area > maxSpotOrDotArea)
{
if (imageContent == null)
{
imageContent = content;
}
imageContent.Include(content.Right, content.Bottom);
imageContent.Include(content.Left, content.Top);
}
else
{
// Here it's better we increase values of the incrementers.
// Depending on size of spots/dots.
// It should increase performance.
if (dx < content.Width) dx = content.Width;
if (dy < content.Height) dy = content.Height;
}
}
}
}
// Everything is done.
this.progressBar1.Value = this.progressBar1.Maximum;
// If image content has been detected
// then we save the information
if (imageContent != null)
{
this.left = imageContent.Left;
this.top = imageContent.Top;
this.right = imageContent.Right;
this.bottom = imageContent.Bottom;
}
this.pictureBox1.Invalidate();
}
internal static readonly int Black = Color.Black.ToArgb();
internal static readonly int White = Color.White.ToArgb();
int top;
int bottom;
int left;
int right;
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (pictureBox1.Image == null)
{
return;
}
int xMax = pictureBox1.Image.Width;
int yMax = pictureBox1.Image.Height;
int startX = this.left;
int startY = this.top;
int endX = this.right;
int endY = this.bottom;
int picWidth = pictureBox1.Size.Width;
int picHeight = pictureBox1.Size.Height;
float imageRatio = xMax / (float)yMax; // image W:H ratio
float containerRatio = picWidth / (float)picHeight; // container W:H ratio
if (imageRatio >= containerRatio)
{
// horizontal image
float scaleFactor = picWidth / (float)xMax;
float scaledHeight = yMax * scaleFactor;
// calculate gap between top of container and top of image
float filler = Math.Abs(picHeight - scaledHeight) / 2;
//float filler = 0;
startX = (int)(startX * scaleFactor);
endX = (int)(endX * scaleFactor);
startY = (int)((startY) * scaleFactor + filler);
endY = (int)((endY) * scaleFactor + filler);
}
else
{
// vertical image
float scaleFactor = picHeight / (float)yMax;
float scaledWidth = xMax * scaleFactor;
float filler = Math.Abs(picWidth - scaledWidth) / 2;
startX = (int)((startX) * scaleFactor + filler);
endX = (int)((endX) * scaleFactor + filler);
startY = (int)(startY * scaleFactor);
endY = (int)(endY * scaleFactor);
}
//if (_once)
//Rectangle ee = new Rectangle(35, 183, 405, 157);
Rectangle ee = new Rectangle(startX, startY, endX - startX, endY - startY);
System.Diagnostics.Debug.WriteLine(startX + ", " + startY + ", " + (endX - startX) + ", " + (endY - startY));
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, ee);
}
//_once = false;
}
}
// This class is similar to System.Drawing.Region class
// except that its only rectangular.
// Because all we need is to draw a rectagnle
// around the image this property must
// make it faster, at least I hope so.
class BlackContent
{
internal void FloodFill(Bitmap image)
{
FloodFillPrivate(image, this.left + 1, this.top, 0);
}
// Legendary Flood-Fill algorithm.
// Quite often it ends up with StackOverFlow exception.
// But this class and its rectangularity property
// must prevent this disaster.
// In my experiments I didn't encounter incidents.
void FloodFillPrivate(Bitmap image, int x, int y, int level)
{
if (x >= image.Width || x < 0 || y >= image.Height || y < 0 || this.Contains(x, y) || image.GetPixel(x, y).ToArgb() != Form1.Black)
{
return;
}
this.Include(x, y);
FloodFillPrivate(image, x, y - 1, level + 1);
FloodFillPrivate(image, x, y + 1, level + 1);
FloodFillPrivate(image, x - 1, y, level + 1);
FloodFillPrivate(image, x + 1, y, level + 1);
}
internal BlackContent(int x, int y)
{
this.left = x;
this.right = x;
this.top = y;
this.bottom = y;
}
internal void Include(int x, int y)
{
if (x < this.left)
{
this.left = x;
}
if (this.right < x)
{
this.right = x;
}
if (this.bottom < y)
{
this.bottom = y;
}
if (y < this.top)
{
this.top = y;
}
}
internal bool Contains(int x, int y)
{
return !(x < this.left || x > this.right || y < this.top || y > this.bottom);
}
int left;
internal int Left { get { return this.left; } }
int top;
internal int Top { get { return this.top; } }
int right;
internal int Right { get { return this.right; } }
int bottom;
internal int Bottom { get { return this.bottom; } }
internal int Area
{
get
{
return Width * Height;
}
}
internal int Width
{
get
{
return (this.right - this.left + 1);
}
}
internal int Height
{
get
{
return (this.bottom - this.top + 1);
}
}
}
}
我用ProgressBar看了表演。这个比较快我还必须提到您的图片太大了。
关于c# - 查找图像内容并在其周围绘制矩形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43722439/
我正在尝试将外框内的框(坐标)放入。我已经使用交集联合方法完成了工作,并且我希望其他方法也可以这样做。 另外,能否请您告诉我如何比较这两个内盒? 最佳答案 通过比较边界框和内部框的左上角和右下角的坐标
我希望输出看起来像这样: 如何安排这些循环以获得两个三角形数字模式?我该如何改进我的代码。 JAVA 中的新功能:-) for (int i = 1; icount; num--) {
我需要将 map 边界存储在 MySQL 数据库中。我花了一些时间在地理空间扩展的文档上,但是学习所有相关信息(WKT、WKB 等)很困难,而且就我而言没有必要。我只需要一种方法来存储坐标矩形并稍后将
在 gnuplot 中,我可以通过绘制一个矩形 set object rect from x0,y0 to x1,y1 如何从文件中读取坐标 x0,x1,y0,y1? 最佳答案 一种方法是将设置矩形的
我正在尝试创建一个填充了水平线或垂直线的矩形。 矩形的宽度是动态的,所以我不能使用图像刷。 如果有人知道任何解决方案,请告诉我。 最佳答案 我想出了一个直接的方法来做到这一点;最后,我使用以下视觉画笔
这个 SVG 在所有浏览器中看起来都很模糊,在所有缩放级别。 在 Chrome、Safari 和 Firefox 中,它看起来像这样: 如果放大,您可以看到笔画有两个像素的宽度,即使默认笔画
我正在尝试在ggplot2图上添加多个阴影/矩形。在这个可重现的示例中,我只添加了3,但是使用完整数据可能需要总计一百。 这是我的原始数据的子集-在名为temp的数据框中-dput在问题的底部:
我有一个包含驻留在 Viewport3D 中的 3D 对象的应用程序,我希望用户能够通过在屏幕上拖动一个矩形来选择它们。 我尝试在 Viewport3D 上应用 GeometryHitTestPara
如何才能使 WPF 矩形的顶角变成圆角? 我创建了一个边框并设置了 CornerRadius 属性,并在边框内添加了矩形,但它不起作用,矩形不是圆角的。 最佳答案 您遇到的问题是矩形“溢
我正在尝试使用此 question 中的代码旋转 Leaflet 矩形。 rotatePoints (center, points, yaw) { const res = [] const a
我有以下图像。 this image 我想删除数字周围的橙色框/矩形,并保持原始图像干净,没有任何橙色网格/矩形。 以下是我当前的代码,但没有将其删除。 Mat mask = new Mat(); M
我发现矩形有些不好笑: 比方说,给定的是左、上、右和下坐标的值,所有这些坐标都旨在包含在内。 所以,计算宽度是这样的: width = right - left + 1 到目前为止,一切都很合乎逻辑。
所以,我一直在学习 Java,但我还是个新手,所以请耐心等待。我最近的目标是图形化程序,这次是对键盘控制的测试。由于某种原因,该程序不会显示矩形。通常,paint() 会独立运行,但由于某种原因它不会
我正在阅读 website 中的解决方案 3 (2D)并试图将其翻译成java代码。 java是否正确请评论。我使用的是纬度和经度坐标,而不是 x 和 y 坐标(注意:loc.getLongitude
我似乎无法删除矩形上的边框!请参阅下面的代码,我正在使用 PDFannotation 创建链接。这些链接都有效,但每个矩形都有一个边框。 PdfAnnotation annotation; Recta
如何在保持原始位图面积的同时将位图旋转给定的度数。即,我旋转宽度:100,高度:200 的位图,我的最终结果将是一个更大的图像,但旋转部分的面积仍然为 100*200 最佳答案 图形转换函数非常适合这
我创建了矩形用户控件,我在我的应用程序中使用了这个用户控件。在我的应用程序中,我正在处理图像以进行不同的操作,例如从图像中读取条形码等。这里我有两种处理图像的可能性,一种正在处理整个图像,另一个正在处
好的,我该如何开始呢? 我有一个应用程序可以在屏幕上绘制一些形状(实际上是几千个)。它们有两种类型:矩形和直线。矩形有填充,线条有描边 + 描边厚度。 我从两个文件中读取数据,一个是顶部的数据,一个是
简而言之: 我正在致力于使用 AI 和 GUI 创建纸牌游戏。用户的手显示在游戏界面上,我尚未完成界面,但我打算将牌面图像添加到屏幕上的矩形中。我没有找到 5 种几乎相同的方法,而是找到了一篇类似的文
我遇到了麻烦。我正在尝试使用用户输入的数组列表创建条形图。我可以创建一个条,但只会创建一个条。我需要所有数组输入来创建一个条。 import java.awt.Color; import java.a
我是一名优秀的程序员,十分优秀!