在下面的图像中我有一个不光滑的表面(我不知道它的术语)。我会在白色物体的上方做一条直线,但它有粗糙的表面。如果可见,整个物体不是水平的它是倾斜的,它的角度可以预先定义,并逐个变化。
我需要一条直线来表示这个粗糙的表面(在红色框中),这是一个平均值(或者可能不是平均值)。 最后我需要一条曲线y = mx + b。你能否提出你的想法或帮助我如何处理解决方案?
答案 0 :(得分:1)
以下是使用ImageMagick的答案的大致轮廓,ImageMagick安装在大多数Linux发行版上,可用于OSX和Windows。
首先通过在边缘涂抹黑色来清洁图像,从侧面涂抹2.5%,然后阈值处理为纯黑色和白色。同时转为PNG格式以避免丢失和错误。我通常只是通过裁剪一些边缘来修剪图像以摆脱你的锯齿状顶边,但这会使你的答案的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 $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坐标。
我对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
答案 1 :(得分:1)
这是基于我的另一个答案,但我把它作为另一个单独的答案,因为它使用完全不同的技术。在这里,我使用 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;
}
}
}
}