如何检测带有背景网格的黑白图像中的曲线?
How to detect a curve in a B&W image with a background grid?
我正在努力从这张图片中提取曲线:
它代表我不想触及的科学数据,以避免引入错误,所以我不能只是在绘图数字化仪中手动重画线:我想自动提取曲线,然后我将其输入绘图数字化仪。
我尝试以编程方式在图像上绘制白色网格,尝试与网格完全重叠并将其删除;但不幸的是,这是对纸张的扫描sheet,因此网格并不完美,因此行间距并不总是相同的。
用于网格的代码:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="generator" content="PSPad editor, www.pspad.com">
<title></title>
<script>
sourcename = "converted";
originalHeight = 0;
originalWidth = 0;
function renderImage()
{
var canvas = document.getElementById("grid");
var context = canvas.getContext("2d");
var image = new Image();
image.onload = function() {
selectedimage = image;
originalHeight = image.height;
originalWidth = image.width;
canvas.height = image.height;
canvas.width = image.width;
context.drawImage(image, 0, 0, canvas.width, canvas.height);
xstep = document.getElementById("rngXstep").value*1;
ystep = document.getElementById("rngYstep").value*1;
xoffset = document.getElementById("rngxoffset").value*1;
yoffset = document.getElementById("rngyoffset").value*1;
strokewidth = document.getElementById("rngstrokewidth").value*1;
color = document.getElementById("color").value;
document.getElementById("xstep").value = document.getElementById("rngXstep").value*1
document.getElementById("ystep").value = document.getElementById("rngYstep").value*1
document.getElementById("xoffset").value = document.getElementById("rngxoffset").value*1
document.getElementById("yoffset").value = document.getElementById("rngyoffset").value*1
document.getElementById("strokewidth").value = document.getElementById("rngstrokewidth").value*1
drawGrid(context,0,0,0,xoffset,yoffset,xstep,ystep,0,0,color,strokewidth);
};
image.src = "chart-test-2.png";
}
var drawGrid = function(context, w, h, id, xoff, yoff, xstep, ystep, xmax, ymax, color,width) {
for (var x = 0; x <= originalWidth; x += xstep) {
context.moveTo(1 + x + xoff, 0 + yoff);
context.lineTo(1 + x + xoff, originalHeight + yoff);
}
for (var x = 0; x <= originalHeight; x += ystep) {
context.moveTo(0, 1 + x + yoff);
context.lineTo(originalWidth, 1 + x + yoff);
}
context.strokeStyle = color;
context.lineWidth = width;
context.stroke();
}
</script>
</head>
<body>
x step: <input type="text" id="xstep" name="xstep" value=4.1>
<input type="range" id="rngXstep" name = "rngXstep" value="4.1" onchange="renderImage()" step=.1><br>
y step:<input type="text" id="ystep" name="ystep" value=4.1>
<input type="range" id="rngYstep" name = "rngYstep" value="4.1" onchange="renderImage()" step=.1><br>
x offset: <input type="text" id="xoffset" name="xoffset" value=0>
<input type="range" id="rngxoffset" name = "rngxoffset" value="0" onchange="renderImage()" step=.1><br>
y offset:<input type="text" id="yoffset" name="yoffset" value=0>
<input type="range" id="rngyoffset" name = "rngyoffset" value="0" onchange="renderImage()" step=.1><br>
width: <input type="text" id="strokewidth" name="strokewidth" value=2>
<input type="range" id="rngstrokewidth" name = "rngstrokewidth" value="2" onchange="renderImage()" step=.1><br>
color: <input type="text" id="color" name="color" value="white"> (red, white, blue,...)<br>
<canvas id="grid"></canvas><br>
<img id="chart" name="chart" src="chart-test-2.png" ><br>
</body>
</html>
直接将图像放入绘图数字化仪是行不通的,因为曲线具有与网格相同的颜色,而绘图数字化仪使用颜色自动检测曲线。
然后我决定尝试一种不同的方法:寻找图像中最粗的线;我可以尝试编写自己的算法,扫描每个像素并确定哪个像素有更多的黑色邻居……但我可能会重新发明轮子;我问 ImageMagick 作者他的程序是否能够做这样的事情,但他说不能。
然后我发现OpenCV实现了一些算法,比如Hough Transform,Contour detector,可能还有很多其他的;但我是 OpenCV 的新手,所以我需要一些提示,了解哪些函数最适合我的需要。
可以Hough transform detect also curved lines, or only segments? Can contour detector also detect open curves or just closed lines? Or should I use canny edge detection吗?
但另一个问题是我需要 javascript 个示例,而不是 C++ 或 Python。
但我对任何 javascript 解决方案持开放态度,OpenCV 不是唯一的选择。
经过多次实验(基于these examples)和测试,我最终得到了这个代码:
let src = cv.imread('canvasInput');
let dst = new cv.Mat();
let M = cv.Mat.ones(3, 3, cv.CV_8U);
let anchor = new cv.Point(-1, -1);
cv.dilate(src, dst, M, anchor, 1, cv.BORDER_CONSTANT, cv.morphologyDefaultBorderValue());
cv.imshow('canvasOutput', dst);
src.delete(); dst.delete(); M.delete();
可能通过连接多个进一步的过滤器可以获得更好的结果,无论如何我已经通过这种方式获得了可接受的结果。
完整源图片:
已编辑,但未修改数据:
由上面的代码处理(“扩张”):
提交给webplotdigitizer(看右边的设置,黄色区域是webplotdigitizer唯一处理过的):
webplotdigitizer 的原始处理;出现了一些杂散点,但可以很容易地手动删除它们:
已清理:
重新绘制:
注意:原图中水平和垂直比例不同:水平一个是4x w.r.t垂直一个;垂直调整大小 4 倍我得到:
将最终图像叠加在源图像上,我们可以看到它完美匹配:
当然,在同一个源代码中进行数字化会更好,但这是另一回事了...
我正在努力从这张图片中提取曲线:
它代表我不想触及的科学数据,以避免引入错误,所以我不能只是在绘图数字化仪中手动重画线:我想自动提取曲线,然后我将其输入绘图数字化仪。
我尝试以编程方式在图像上绘制白色网格,尝试与网格完全重叠并将其删除;但不幸的是,这是对纸张的扫描sheet,因此网格并不完美,因此行间距并不总是相同的。
用于网格的代码:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="generator" content="PSPad editor, www.pspad.com">
<title></title>
<script>
sourcename = "converted";
originalHeight = 0;
originalWidth = 0;
function renderImage()
{
var canvas = document.getElementById("grid");
var context = canvas.getContext("2d");
var image = new Image();
image.onload = function() {
selectedimage = image;
originalHeight = image.height;
originalWidth = image.width;
canvas.height = image.height;
canvas.width = image.width;
context.drawImage(image, 0, 0, canvas.width, canvas.height);
xstep = document.getElementById("rngXstep").value*1;
ystep = document.getElementById("rngYstep").value*1;
xoffset = document.getElementById("rngxoffset").value*1;
yoffset = document.getElementById("rngyoffset").value*1;
strokewidth = document.getElementById("rngstrokewidth").value*1;
color = document.getElementById("color").value;
document.getElementById("xstep").value = document.getElementById("rngXstep").value*1
document.getElementById("ystep").value = document.getElementById("rngYstep").value*1
document.getElementById("xoffset").value = document.getElementById("rngxoffset").value*1
document.getElementById("yoffset").value = document.getElementById("rngyoffset").value*1
document.getElementById("strokewidth").value = document.getElementById("rngstrokewidth").value*1
drawGrid(context,0,0,0,xoffset,yoffset,xstep,ystep,0,0,color,strokewidth);
};
image.src = "chart-test-2.png";
}
var drawGrid = function(context, w, h, id, xoff, yoff, xstep, ystep, xmax, ymax, color,width) {
for (var x = 0; x <= originalWidth; x += xstep) {
context.moveTo(1 + x + xoff, 0 + yoff);
context.lineTo(1 + x + xoff, originalHeight + yoff);
}
for (var x = 0; x <= originalHeight; x += ystep) {
context.moveTo(0, 1 + x + yoff);
context.lineTo(originalWidth, 1 + x + yoff);
}
context.strokeStyle = color;
context.lineWidth = width;
context.stroke();
}
</script>
</head>
<body>
x step: <input type="text" id="xstep" name="xstep" value=4.1>
<input type="range" id="rngXstep" name = "rngXstep" value="4.1" onchange="renderImage()" step=.1><br>
y step:<input type="text" id="ystep" name="ystep" value=4.1>
<input type="range" id="rngYstep" name = "rngYstep" value="4.1" onchange="renderImage()" step=.1><br>
x offset: <input type="text" id="xoffset" name="xoffset" value=0>
<input type="range" id="rngxoffset" name = "rngxoffset" value="0" onchange="renderImage()" step=.1><br>
y offset:<input type="text" id="yoffset" name="yoffset" value=0>
<input type="range" id="rngyoffset" name = "rngyoffset" value="0" onchange="renderImage()" step=.1><br>
width: <input type="text" id="strokewidth" name="strokewidth" value=2>
<input type="range" id="rngstrokewidth" name = "rngstrokewidth" value="2" onchange="renderImage()" step=.1><br>
color: <input type="text" id="color" name="color" value="white"> (red, white, blue,...)<br>
<canvas id="grid"></canvas><br>
<img id="chart" name="chart" src="chart-test-2.png" ><br>
</body>
</html>
直接将图像放入绘图数字化仪是行不通的,因为曲线具有与网格相同的颜色,而绘图数字化仪使用颜色自动检测曲线。
然后我决定尝试一种不同的方法:寻找图像中最粗的线;我可以尝试编写自己的算法,扫描每个像素并确定哪个像素有更多的黑色邻居……但我可能会重新发明轮子;我问 ImageMagick 作者他的程序是否能够做这样的事情,但他说不能。
然后我发现OpenCV实现了一些算法,比如Hough Transform,Contour detector,可能还有很多其他的;但我是 OpenCV 的新手,所以我需要一些提示,了解哪些函数最适合我的需要。
可以Hough transform detect also curved lines, or only segments? Can contour detector also detect open curves or just closed lines? Or should I use canny edge detection吗?
但另一个问题是我需要 javascript 个示例,而不是 C++ 或 Python。
但我对任何 javascript 解决方案持开放态度,OpenCV 不是唯一的选择。
经过多次实验(基于these examples)和测试,我最终得到了这个代码:
let src = cv.imread('canvasInput');
let dst = new cv.Mat();
let M = cv.Mat.ones(3, 3, cv.CV_8U);
let anchor = new cv.Point(-1, -1);
cv.dilate(src, dst, M, anchor, 1, cv.BORDER_CONSTANT, cv.morphologyDefaultBorderValue());
cv.imshow('canvasOutput', dst);
src.delete(); dst.delete(); M.delete();
可能通过连接多个进一步的过滤器可以获得更好的结果,无论如何我已经通过这种方式获得了可接受的结果。
完整源图片:
已编辑,但未修改数据:
由上面的代码处理(“扩张”):
提交给webplotdigitizer(看右边的设置,黄色区域是webplotdigitizer唯一处理过的):
webplotdigitizer 的原始处理;出现了一些杂散点,但可以很容易地手动删除它们:
已清理:
重新绘制:
注意:原图中水平和垂直比例不同:水平一个是4x w.r.t垂直一个;垂直调整大小 4 倍我得到:
将最终图像叠加在源图像上,我们可以看到它完美匹配:
当然,在同一个源代码中进行数字化会更好,但这是另一回事了...