目的
カメラのセンサーサイズを紙と定規とEXIFで計算してみようを見ていて、
スマホならともかく、カメラだと長さ測るの無理じゃないと思って考えてみた。
やったこと
- 印刷したArUcoマーカーを10cm前後ずらして撮影
- 下記値を使って1Pixelあたりのサイズを算出
- 印刷したマーカーのサイズ[cm]
- それぞれのマーカーのPixelサイズ[px]
- 使用したレンズの焦点距離
結果
カタログで計算した値より小さくなってしまいました。
マーカーの位置が適当だったので、その影響かもです。
(マーカーが正面向いていないとか、レンズ中心じゃないと式が成り立たないとか)
あと、近いので実効焦点距離も影響しているかもです。
- カタログから算出: 0.003756946[mm](サイズ: 17.3mm×13.0mm, 画素数: 4608×3456より)
- 測定値から算出: 0.00363212270430662[mm]
詳細
マーカーの準備
適当にマーカを生成し印刷。サイズを測る。ここでは対角線の長さを測った(特に意図はない)。
角は測りにくかったので、辺の長さ(5.075cm)を測って計算。
import cv2
from cv2 import aruco
dictionary = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)
marker = aruco.drawMarker(dictionary, 4, 112)
dict_aruco = aruco.Dictionary_get(aruco.DICT_4X4_50)
img_mark = aruco.drawMarker(dict_aruco, 4, 180)
cv2.imwrite("aruco_maker.png", img_mark)
マーカーを撮影
適当にマーカーを配置し複数枚撮影。その後10cm移動させて再度撮影。
サンプル


Pixelサイズを算出
撮影した画像を適当に配置。対角線のピクセル数を算出。何となく対角線2つを算出して平均。
import cv2
from cv2 import aruco
import matplotlib.pyplot as plt
from glob import glob
import os
import os.path
import numpy as np
dict_aruco = aruco.Dictionary_get(aruco.DICT_4X4_50)
parameters = aruco.DetectorParameters_create()
for fn in glob("image_sensor/*.jpg"):
# print(fn)
img = cv2.imread(fn)
# plt.imshow(img[...,::-1])
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
corners, ids, rejectedImgPoints = aruco.detectMarkers(gray, dict_aruco, parameters=parameters)
if ids is None:
continue
cornerUL = corners[0][0][0]
cornerUR = corners[0][0][1]
cornerBR = corners[0][0][2]
cornerBL = corners[0][0][3]
center = [ (cornerUL[0]+cornerBR[0])/2 , (cornerUL[1]+cornerBR[1])/2 ]
l1 = np.sqrt(np.sum((cornerUL - cornerBR) ** 2))
l2 = np.sqrt(np.sum((cornerUR - cornerBL) ** 2))
print(fn, center, (l1 + l2) / 2)
img_markers = aruco.drawDetectedMarkers(img.copy(), corners, ids)
# plt.imshow(img_markers[...,::-1])
dn, bn = os.path.split(fn)
bn, ext = os.path.splitext(bn)
ofn = os.path.join(dn, "marker", f"{bn}_marker{ext}")
os.makedirs(os.path.dirname(ofn), exist_ok=True)
cv2.imwrite(ofn, img_markers)
複数撮った対角線の長さを平均。
近い距離の写真複数撮ったつもりだけど、結局2枚しかなかった・・・
- 近いとき
p1: 1797.411194
- 遠いとき
p2: 1089.5224
image_sensor\P5260067.JPG [2260.5, 2003.0] 1797.4017333984375
image_sensor\P5260068.JPG [2260.5, 2002.0] 1797.420654296875
image_sensor\P5260071.JPG [2271.5, 1961.5] 1090.2025146484375
image_sensor\P5260072.JPG [2273.0, 1960.0] 1089.5416259765625
image_sensor\P5260076.JPG [2272.0, 1960.5] 1089.177001953125
image_sensor\P5260077.JPG [2272.5, 1960.5] 1089.5181884765625
image_sensor\P5260078.JPG [2272.0, 1960.5] 1089.5185546875
image_sensor\P5260079.JPG [2272.5, 1960.5] 1089.176513671875
撮像時の関係
- L
- 撮像した物体。今回はマーカーのサイズ
- d
- マーカーを移動させた距離。今回はカメラを10cm移動させたけど
- f
- 焦点距離。今回は14mm
- $a_1$, $a_2$
- レンズから物体までの距離
- $b_1$, $b_2$
- レンズから像(=イメージセンサー)までの距離。
この絵ではレンズ位置は固定だけど、実際はレンズが動いてイメージセンサ上に像ができているはず - $l_1$, $l_2$
- 像の大きさ。$p_1, p_2$と比例するはず
図から下記関係を導出
変数は6個なので、1つ式余分だけど。。。
sympyで解く
import sympy as sy
sy.init_printing(use_unicode=False, wrap_line=True)
l1, l2, a1, a2, b1, b2, p1, p2, L, f, d = sy.symbols('l_1 l_2 a_1 a_2 b_1 b_2 p_1 p_2 L f d')
eq1 = sy.Eq(l1/l2, (b1-f)/(b2-f))
eq2 = sy.Eq(1/f, 1/a1 + 1/b1)
eq3 = sy.Eq(1/f, 1/a2 + 1/b2)
eq4 = sy.Eq(l2/L, b2/a2)
eq5 = sy.Eq(l1/L, b1/a1)
eq6 = sy.Eq(p1/p2, l1/l2)
eq7 = sy.Eq(a2, a1 + d)
dec = {f: 1.4, p1: 1797.411194, p2: 1089.5224, d: 10, L: sy.sqrt(2 * 5.075 ** 2)}
eqs = [eq1, eq2, eq3, eq4, eq5, eq6, eq7]
eqs = [e.subs(dec) for e in eqs]
sol = sy.solve(eqs)
for n,v in sol[0].items():
display(sy.Eq(n, v))
print(f"1Pixelあたりのサイズ: {(l1/p1).subs(dec).subs(sol[0])}mm")
𝑎1=16.7911519610804
𝑎2=26.7911519610804
𝑏1=1.52734589359888
𝑏2=1.4771922440937
𝑙1=0.652841800670228
𝑙2=0.395727904589064
1Pixelあたりのサイズ: 0.000363212270430662mm