用粗线定义直线?
define straight line with rough line?
在下图中,我有一个不光滑的表面(我不知道它的术语)。我会在白色物体上方画一条直线,但它有粗糙的 surface.as 可见,整个物体不是水平的,是倾斜的,角度可以预定义,可以逐图变化。
我需要一条代表这个粗糙表面(在红色框中)的直线,它是一个平均值(或者可能不是平均值)。
最后我需要一条曲线 y=mx+b。您能否提出您的想法或帮助我如何解决问题。
这是一个粗略的答案,使用安装在大多数 Linux 发行版上的 ImageMagick,可用于 OSX 和 Windows。
首先清理图像,方法是在从侧面向内绘制 2.5% 的所有边缘周围的黑色,然后将阈值化为纯黑色和白色。同时转为 PNG 格式以避免丢失和错误。我通常只是 trim 通过从边缘裁剪一些图像来去除锯齿状的顶部边缘,但这会扭曲你答案的 y 坐标,所以我只是用黑色覆盖它。
然后切成 1 像素宽的垂直条。这会产生 394 个名为 vbar-0000.png
到 bar-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 ;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 坐标。
我对 GNUplot
没什么好感,但我创建了一个名为 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
这是基于我的另一个答案,但我把它作为另一个单独的答案,因为它使用了完全不同的技术。在这里,我使用 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;
}
}
}
}
在下图中,我有一个不光滑的表面(我不知道它的术语)。我会在白色物体上方画一条直线,但它有粗糙的 surface.as 可见,整个物体不是水平的,是倾斜的,角度可以预定义,可以逐图变化。
我需要一条代表这个粗糙表面(在红色框中)的直线,它是一个平均值(或者可能不是平均值)。 最后我需要一条曲线 y=mx+b。您能否提出您的想法或帮助我如何解决问题。
这是一个粗略的答案,使用安装在大多数 Linux 发行版上的 ImageMagick,可用于 OSX 和 Windows。
首先清理图像,方法是在从侧面向内绘制 2.5% 的所有边缘周围的黑色,然后将阈值化为纯黑色和白色。同时转为 PNG 格式以避免丢失和错误。我通常只是 trim 通过从边缘裁剪一些图像来去除锯齿状的顶部边缘,但这会扭曲你答案的 y 坐标,所以我只是用黑色覆盖它。
然后切成 1 像素宽的垂直条。这会产生 394 个名为 vbar-0000.png
到 bar-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 ;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 坐标。
我对 GNUplot
没什么好感,但我创建了一个名为 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
这是基于我的另一个答案,但我把它作为另一个单独的答案,因为它使用了完全不同的技术。在这里,我使用 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;
}
}
}
}