【问题标题】:define straight line with rough line?用粗线定义直线?
【发布时间】:2016-02-18 17:43:03
【问题描述】:

在下图中,我有一个不光滑的表面(我不知道它的术语)。我会在白色物体的上方画一条直线,但它有粗糙的表面。可见,整个物体不是水平的并且它是倾斜的,它的角度可以预定义和逐张变化。

我需要一条直线来表示这个粗糙表面(在红色框中),它是一个平均值(或者可能不是平均值)。 最后我需要一条曲线 y=mx+b。您能否提出您的想法或帮助我应该如何处理解决方案。

【问题讨论】:

  • 什么操作系统?什么工具?你有创建图像的数字数据吗?您还有其他示例图片吗?
  • 所以您正在寻找line of best fit
  • 它曾经旋转过吗?它总是顶部边缘吗?白色形状会改变大小或位置吗?它有时会部分超出框架吗?有时会有多个对象吗?

标签: object image-processing line signal-processing


【解决方案1】:

这里是一个粗略的答案大纲,使用安装在大多数 Linux 发行版上并且可用于 OSX 和 Windows 的 ImageMagick。

首先通过在从侧面 2.5% 的边缘周围填充黑色来清理图像,然后将阈值处理为纯黑色和白色。也移动到 PNG 格式以避免丢失和错误。我通常会通过裁剪一些边缘来修剪图像以去除锯齿状的顶部边缘,但这会扭曲你答案的 y 坐标,所以我只是用黑色覆盖它。

然后切成 1 像素宽的垂直条。这会产生 394 个文件,名为 vbar-0000.pngbar-0393.png

顺便说一句,它们看起来像这样:

然后遍历垂直条寻找第一个白色像素以获得 y 坐标,因为我们已经从我们正在处理的垂直条的编号知道 x 坐标。抑制任何没有白色像素的线条的输出,否则它们会扭曲曲线拟合。我将文件转换为文本以执行此操作 - 请参阅末尾的注释。

#!/bin/bash

# Pre-processing cleanup
# Blacken in 2.5% around all edges and threshold image to pure black and white, and move to PNG to avoid losses
convert thing.jpg -background black -gravity center -crop 95x95% -extent 374x295 -colorspace gray -threshold 50% +repage thing.png

# Get height
h=$(identify -format "%h" thing.png)

# Slice into 1-pixel wide vertical bars
convert thing.png -crop 1x vbar-%04d.png

# Iterate through all bars finding the first white pixel in each
x=1
for f in vbar*png; do
   y=$(convert "$f" -depth 8 txt: | awk -F'[,:]' '/#FFFFFF/{print $2;exit}')
   # Don't output points for vbars with no white in them - they will skew the curve fitting
   if [ ! -z $y ]; then
      # Measure from bottom left instead of top-left
      ((y=h-y))
      echo $x $y
   fi
   ((x++))
done

产生这个输出:

51 207
52 208
53 209
54 209
55 209
56 209
57 209
58 209
59 209
60 209
61 209
62 209
63 210
64 212
65 212
66 212
67 212
68 212
69 210
70 210
71 210
72 209
73 209
74 209
75 209
76 209
77 207
78 207
79 207
80 206
81 206
82 206
83 205
84 205
85 203
86 203
87 203
88 202
89 202
90 202
91 202
92 200
93 200
94 200
95 200
96 199
97 199
98 199
99 197
100 197
101 197
102 197
103 196
104 196
105 196
106 196
107 196
108 194
109 194
110 194
111 193
112 192
113 192
114 193
115 192
116 192
117 192
118 192
119 192
120 192
121 190
122 189
123 189
124 189
125 187
126 187
127 186
128 184
129 184
130 183
131 183
132 184
133 184
134 186
135 186
136 186
137 186
138 187
139 187
140 187
141 189
142 189
143 189
144 189
145 190
146 190
147 190
148 190
149 190
150 190
151 190
152 190
153 190
154 189
155 189
156 187
157 185
158 184
159 184
160 184
161 183
162 183
163 180
164 180
165 180
166 180
167 179
168 179
169 176
170 174
171 174
172 174
173 174
174 174
175 174
176 177
177 177
178 177
179 179
180 180
181 180
182 180
183 180
184 181
185 181
186 181
187 181
188 181
189 180
190 180
191 180
192 180
193 179
194 179
195 179
196 176
197 171
198 171
199 173
200 173
201 173
202 173
203 173
204 173
205 173
206 173
207 173
208 173
209 173
210 173
211 173
212 173
213 173
214 173
215 171
216 170
217 170
218 170
219 167
220 167
221 163
222 164
223 164
224 164
225 166
226 167
227 167
228 167
229 167
230 167
231 169
232 169
233 169
234 169
235 169
236 167
237 167
238 167
239 167
240 167
241 167
242 166
243 166
244 164
245 163
246 163
247 163
248 161
249 163
250 163
251 163
252 163
253 163
254 163
255 163
256 163
257 163
258 163
259 163
260 163
261 163
262 163
263 163
264 163
265 163
266 163
267 161
268 161
269 161
270 161
271 161
272 161
273 158
274 158
275 158
276 158
277 157
278 157
279 157
280 156
281 156
282 156
283 156
284 154
285 154
286 153
287 151
288 151
289 150
290 148
291 148
292 148
293 145
294 144
295 144
296 144
297 140
298 140
299 141
300 140
301 140
302 143
303 143
304 141
305 141
306 144
307 135
308 134
309 134
310 133
311 133
312 134
313 134
314 134
315 121
316 122

您可以将其输入GNUplot 以获得y=mx + c 的系数。

我已经用红色标记了识别点的位置:

当我将垂直条转换为文本时,在上面,我使用了这个:

convert bar-????.png -depth 8 txt:

结果是这样的:

convert vbar-0145.png -depth 8 txt:

# ImageMagick pixel enumeration: 1,295,255,gray
0,0: (0,0,0)  #000000  gray(0)
0,1: (0,0,0)  #000000  gray(0)
0,2: (0,0,0)  #000000  gray(0)
0,3: (0,0,0)  #000000  gray(0)
0,4: (0,0,0)  #000000  gray(0)
0,5: (0,0,0)  #000000  gray(0)
0,6: (0,0,0)  #000000  gray(0)
0,7: (0,0,0)  #000000  gray(0)
0,8: (0,0,0)  #000000  gray(0)
0,9: (0,0,0)  #000000  gray(0)
0,10: (0,0,0)  #000000  gray(0)
0,11: (0,0,0)  #000000  gray(0)
0,12: (0,0,0)  #000000  gray(0)
...
...
0,101: (0,0,0)  #000000  gray(0)
0,102: (0,0,0)  #000000  gray(0)
0,103: (0,0,0)  #000000  gray(0)
0,104: (0,0,0)  #000000  gray(0)
0,105: (65535,65535,65535)  #FFFFFF  gray(255)  <--- Here is the first white pixel starting from the top
0,106: (65535,65535,65535)  #FFFFFF  gray(255)
0,107: (65535,65535,65535)  #FFFFFF  gray(255)
0,108: (65535,65535,65535)  #FFFFFF  gray(255)
0,109: (65535,65535,65535)  #FFFFFF  gray(255)
...
...

awk 小脚本只是查找#FFFFFF 的第一次出现,即白色,然后打印该行第一个逗号之后的 y 坐标。

我对@9​​87654338@ 并不感冒,但我创建了一个名为plot.cmd 的命令文件,如下所示:

set title 'Plotted with Gnuplot'
set ylabel 'y-axis'
set xlabel 'x-axis'
f(x)=a*x+b
fit f(x) 'points.txt' using 1:2 via a,b
plot 'points.txt',f(x) with lines linestyle 3
set terminal png large
set output 'graph.png'
set size 1,0.5
replot

然后跑了:

gnuplot plot.cmd

得到了这个

它告诉我方程是

y = -0.26x + 225.2

GNUplot 输出

After 5 iterations the fit converged.
final sum of squares of residuals : 5200.82
rel. change during last iteration : -1.49004e-07

degrees of freedom    (FIT_NDF)                        : 264
rms of residuals      (FIT_STDFIT) = sqrt(WSSR/ndf)    : 4.43848
variance of residuals (reduced chisquare) = WSSR/ndf   : 19.7001

Final set of parameters            Asymptotic Standard Error
=======================            ==========================
a               = -0.260914        +/- 0.003544     (1.358%)
b               = 225.212          +/- 0.705        (0.313%)

请注意,您可以更改 bash 脚本的最后几行以在最后自动完成所有绘图和内容:

...
...
   fi
   ((x++))
done > points.txt

gnuplot plot.cmd

【讨论】:

  • 我从未想过我会看到有人使用 shell 脚本和命令行工具进行任何有用的图像处理。这让我很开心:)
  • 它只是展示了 ImageMagick 的巨大威力与 Unix 哲学相结合,即联合几个强大的工具来完成更大的工作。如果我不一直解释自己并制作小的,易于消化的块,我可能会做一个单线!感谢您的评论。
  • 非常感谢您的出色回答和想法。对于这个图像,它工作得很好。我不得不说我使用 matlab 来完成这项任务。我还有一些其他图片,其中另一个对象出现在当前的白色对象上。你能告诉我如何找到它们的平面吗?我用红线演示。
  • 很高兴它对您有用!我想您将在您的问题中添加另一张图片。如果是这样,请添加一个未标记的图像和一个红色标记的图像,以便我得到一个干净的未标记图像。
  • stackoverflow 不允许我上传 3 张图片。我只能上传 2 张图片。我需要一个平坦的表面用于白色物体下方(您完美地找到了它)和底部的一条线。在链接中,我显示了我需要的线路。 IMAGE LINK
【解决方案2】:

这是基于我的另一个答案,但我将其作为另一个单独的答案,因为它使用完全不同的技术。在这里,我使用 OpenCV 计算机视觉库 而不是 ImageMagick 来获得我在另一个答案上用红色标记的点。然后这些将像其他答案一样被输入 GNUPlot,因为我不想自己解方程:-)

我在 OpenCV 方面不是那么胜任,可能有更快的方法来实现同样的目标。

// Get x,y coordinates of first white pixel in each column of image starting from top
// Mark Setchell

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{

    Mat img = imread("/Users/Mark/tmp/thing.png",IMREAD_GRAYSCALE);
    const uchar white = 255;
    int w =img.cols;
    int h =img.rows;
    for (int x = 0; x < w; ++x)
    {
       for(int y = 0; y < h; ++y)
       {
          unsigned char val=img.at<uchar>(y, x);
          if (val == white) {
              int inverted = h - y;
              cout << x << " " << inverted << endl;
              break;
          }

       }
    }
}

【讨论】:

    猜你喜欢
    • 2017-10-04
    • 1970-01-01
    • 2021-07-06
    • 2017-07-14
    • 2014-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-03
    相关资源
    最近更新 更多