numpyの関数にはnumpy.sumやnumpy.anyなど引数にaxisを指定できる場合があるのだけど、いまいちイメージがつかめなかったのでnp.sumのaxis指定したときの挙動のイメージをお絵描きしました。2次元の行列に限定した話です。以下の図のように「axis=0のときは踏みつぶす」、「axis=1のときは握り潰す」とイメージすることで解決です。あれ、踏み潰すのはどっちだっけ。それでは、おやすみなさい。
numpyの関数にはnumpy.sumやnumpy.anyなど引数にaxisを指定できる場合があるのだけど、いまいちイメージがつかめなかったのでnp.sumのaxis指定したときの挙動のイメージをお絵描きしました。2次元の行列に限定した話です。以下の図のように「axis=0のときは踏みつぶす」、「axis=1のときは握り潰す」とイメージすることで解決です。あれ、踏み潰すのはどっちだっけ。それでは、おやすみなさい。
昼休みの暇つぶしに書いたコードを載せます。
エクセルのアルファベットの列番号を数値に変換する簡単なPythonプログラムになります。
例:
"A"は1
"Z"は26
"AA"は27
アルファベットを数値に変換し、各桁の重みをかけて右から左へ順に足しこんで行くプログラムです。
以下にアルファベットを数値に変換する関数conv(alphabet)とその関数のテストプログラムを示します。
inps = ['A', 'Z', 'AA', 'AZ', 'ZZ'] out_expects = [1, 26, 26+1, 26+26, 26*26+26] def conv(alphabet): total = 0 for n, i in enumerate(alphabet[::-1]): chr_num = ord(i.lower()) chr_A_num = ord('a') total += (1 + chr_num - chr_A_num)* (26**n) return total def test(inps, out_expects): for alphabet, out_expect in zip(inps, out_expects): out = conv(alphabet) print('input:%s, output:%s, out expect:%s'%(alphabet, str(out), str(out_expect))) print('%s' %('OK' if out==out_expect else 'NG')) test(inps, out_expects)
テスト結果
input:A, output:1, out expect:1 OK input:Z, output:26, out expect:26 OK input:AA, output:27, out expect:27 OK input:AZ, output:52, out expect:52 OK input:ZZ, output:702, out expect:702 OK
昆布関数conv(alphabet)がアルファベットを数値に変換します。
出力結果の期待値については以下のように考えるとわかりやすいと思います。
'A'は1
'AA'は26+1
'AZ'は26+26
'BA'は26*2 + 1
'BZ'は26*2 + 26
、、、
'ZZ'は26*26+26
もちろん「conv(alphabet) - 1」とすれば0から始まるインデックスとして使うこともできます。
2進数変換はよく聞くけどこの形の変換は聞いたことがないです。
この形式の数はなんというのでしょうかね?16進数のように数値とアルファベットが混ざっているわけではないのでN進数という言葉ではない気がします。知っている人が入ればコメントください。
以上
XY座標に点A1(Xa1, Ya1), A2(Xa2, Ya2), B1(Xb1, Yb1), B2(Xb2, Yb2)があったとして、
線A1A2 と 線B1B2の交点を数学的に求め、数式があってるか確かめようという話。
以下のように数学的に求めます。
行列Mとその逆行列invMの関係は以下になります。
M = ((a,b),(c,d))
invM = 1/(a*d - b*c) * ((d, -b),(-c, a))
交点を求める自作関数get_line_intersectionで上記で求めた数式を使っています。
from numpy import sin, cos, pi import matplotlib.pyplot as plt from matplotlib.widgets import Slider class point: def __init__(self, x, y): self.x = float(x) self.y = float(y) def get_line_intersection(A1, A2, B1, B2): a = A2.y - A1.y b = -B2.y + B1.y c = A2.x - A1.x d = -B2.x + B1.x C1 = B1.y - A1.y C2 = B1.x - A1.x tmp = a * d - b * c if tmp: invMa = d / tmp invMb = -b / tmp invMc = -c / tmp invMd = a / tmp m = invMa * C1 + invMb * C2 n = invMc * C1 + invMd * C2 if 0<=m<=1 and 0<=n<=1:#両方の線上に点がある場合は交点の座標を返す return point(A1.x + m * (A2.x - A1.x), A1.y + m * (A2.y - A1.y)) else: return point(0, 0)#辺の延長線上に交点がある場合は(0,0)を返すこととする else: return point(0, 0)#逆行列が存在しない時は強制的に(0,0)を返すことにする ################################################################ # 描画 ################################################################ fig, ax = plt.subplots() ax.set_xlim(-11,29) ax.set_ylim(-11,29) ax.set_aspect('equal') plt.subplots_adjust(left=0.25, bottom=0.35) l1, = plt.plot([6, 6], [6 + 5 * cos(pi/2), 6 + 5 * sin(pi/2)]) l2, = plt.plot([12, 12], [12 + 5 * cos(pi/2), 12 + 5 * sin(pi/2)]) l3, = plt.plot([0],[0], marker="o") axcolor = 'lightgoldenrodyellow' axRA = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor) axTA = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor) axRB = plt.axes([0.25, 0.2, 0.65, 0.03], facecolor=axcolor) axTB = plt.axes([0.25, 0.25, 0.65, 0.03], facecolor=axcolor) RA = Slider(axRA, 'RadiusA', 0.0, 20.0, valinit=5.0) TA = Slider(axTA, 'ThetaA', 0.0, 4 * pi, valinit=pi/2) RB = Slider(axRB, 'RadiusB', 0.0, 20.0, valinit=5.0) TB = Slider(axTB, 'ThetaB', 0.0, 4 * pi, valinit=pi/2) def update(val): l1.set_xdata([6, 6 + RA.val * cos(TA.val)]) l1.set_ydata([6, 6 + RA.val * sin(TA.val)]) l2.set_xdata([12, 12 + RB.val * cos(TB.val)]) l2.set_ydata([12, 12 + RB.val * sin(TB.val)]) p = get_line_intersection(point(6,6), point(6+RA.val*cos(TA.val), 6+RA.val*sin(TA.val)), point(12,12), point(12+RB.val*cos(TB.val), 12+RB.val*sin(TB.val))) l3.set_xdata([p.x]) l3.set_ydata([p.y]) fig.canvas.draw_idle() RA.on_changed(update) TA.on_changed(update) RB.on_changed(update) TB.on_changed(update) plt.show()
CPU Core i7 2.0Ghzで計算したが計算時間は100万回やっても5秒くらいらしい
100000 loops, best of 3: 4.76 µs per loop
おそらく、無人島の先生がこの直角三角形のアバウトな角度出しなさいという問題に答える生徒しか使わない気がしますが一応書いておきます。
無人島で直角三角形ABCの角度をアバウトに図りたい時は
角度 = 60 * 高さ / 底辺
を計算します。計算した角度から大まかな誤差を求めます。
誤差は
40°で約20%
20°で約10%
10°で約5%
となります。
この誤差をみて使えるか使えないか判断するとよいでしょう。
誤差が大きい場合は別の手法を使いましょう。
この数式の背景にあるもの:
x が 0 に限りなく近い時以下が成り立ちます。
x = tan(x)
私の疑問として、
というのがあり、それを計算機で計算し誤差を求めた結果、上記数式が使えない使えることを発見しました。
検証は以下のコードでで行いました。
import numpy as np import matplotlib.pyplot as plt def rad_to_degree(rad): return rad * 60 end_degree = 45 xdegree = np.linspace(0.000000000000001,end_degree,100) xrad = np.pi * 2 * xdegree / 360 guess_degree = rad_to_degree(np.tan(xrad)) true_degree = xdegree diff = guess_degree - true_degree fig = plt.figure() ax = fig.add_subplot(111) ax.set_xlabel("x [Degree]", fontsize=14, fontweight='bold') ax.set_ylabel("gosa [%]", fontsize=14, fontweight='bold') ax.plot(xdegree, abs(diff)/true_degree*100) plt.show()
以上です。
前回の続きです。今度は二値画像にk-meansを適用し、細線化?してみます。
二値画像においてk-meansが細線化や情報削減に利用できるのではないかと思えるような結果になりました。今回は、scipyのkmeans2を使用し、代表点の初期配置を領域内に収めることで、細線化?を実現します。
ソース:
(関係のないモジュールをimportしまくってますが無視してください。)
# -*- coding: utf-8 -*- from itertools import combinations from itertools import product import random import re import numpy as np from numpy import arange import matplotlib.pyplot as plt from scipy.ndimage import zoom from scipy.fftpack import fft, ifft from scipy.signal import resample from scipy.spatial import Delaunay import matplotlib.tri as tri from scipy.cluster.vq import kmeans,vq from scipy.cluster.vq import kmeans2,vq def rawObjectToMatrix(ro): ret = [] lines = ro.splitlines() for line in lines: if line.strip()=="": continue ret.append(map(lambda x: 0 if x == " " else 1, line)) whiteRow = [[0 for _ in xrange(len(ret[0]))]] return whiteRow + ret + whiteRow def pb(lst2d): ret = "" for lst in lst2d: lst = map(abs, lst) ret += "".join(map(str, lst)) + "\n" print "" print ret def readMyData(): with open("a.txt", 'r') as fp: text = fp.read() objectRawDataList = re.split(r"\n\n", text) objectRawDataList = filter(lambda x: x != "", objectRawDataList) return map(lambda ro: rawObjectToMatrix(ro), objectRawDataList) lst = readMyData() sampleMatrix = zoom(np.array(lst[5]), 1.5) pb(sampleMatrix) height = sampleMatrix.shape[0] width = sampleMatrix.shape[1] mat = sampleMatrix.copy() for rn, _ in enumerate(mat): for cn, __ in enumerate(_): if mat[rn, cn] == 1 and mat[rn + 1, cn] == 1: mat[rn, cn] = 0 out = mat mat = sampleMatrix.copy() for rn, _ in enumerate(mat): for cn, __ in enumerate(_): if mat[(height - 1) - rn, cn] == 1 and mat[(height - 1) - rn - 1, cn] == 1: mat[(height - 1) - rn, cn] = 0 out |= mat mat = sampleMatrix.copy() for rn, _ in enumerate(mat): for cn, __ in enumerate(_): if mat[rn, cn] == 1 and mat[rn, cn + 1] == 1: mat[rn, cn] = 0 out |= mat mat = sampleMatrix.copy() for rn, _ in enumerate(mat): for cn, __ in enumerate(_): if mat[rn, (width - 1) - cn] == 1 and mat[rn, (width - 1) - cn - 1] == 1: mat[rn, (width - 1) - cn] = 0 out |= mat firstPosFlat = out.flatten().argmax() pos = firstPosFlat / width, firstPosFlat % width startPos = pos code = [] while True: if out[pos[0] - 1, pos[1] ] == 1: out[pos[0] - 1, pos[1] ] = 2 pos = pos[0] - 1, pos[1] code.append((-1, 0)) elif out[pos[0] - 1, pos[1] + 1] == 1: out[pos[0] - 1, pos[1] + 1] = 2 pos = pos[0] - 1, pos[1] + 1 code.append((-1, 1)) elif out[pos[0] , pos[1] + 1] == 1: out[pos[0] , pos[1] + 1] = 2 pos = pos[0] , pos[1] + 1 code.append((0, 1)) elif out[pos[0] + 1, pos[1] + 1] == 1: out[pos[0] + 1, pos[1] + 1] = 2 pos = pos[0] + 1, pos[1] + 1 code.append((1, 1)) elif out[pos[0] + 1, pos[1] ] == 1: out[pos[0] + 1, pos[1] ] = 2 pos = pos[0] + 1, pos[1] code.append((1, 0)) elif out[pos[0] + 1, pos[1] - 1] == 1: out[pos[0] + 1, pos[1] - 1] = 2 pos = pos[0] + 1, pos[1] - 1 code.append((1, -1)) elif out[pos[0] , pos[1] - 1] == 1: out[pos[0] , pos[1] - 1] = 2 pos = pos[0] , pos[1] - 1 code.append((0, -1)) elif out[pos[0] - 1, pos[1] - 1] == 1: out[pos[0] - 1, pos[1] - 1] = 2 pos = pos[0] - 1, pos[1] - 1 code.append((-1, -1)) if pos == startPos: break pr = startPos[0] pc = startPos[1] prlst = [] pclst = [] p = [] for n, c in enumerate(code): dr, dc = c pr += dr pc += dc prlst.append(pr) pclst.append(pc) p.append((pr, pc)) prlst = np.array(prlst) pclst = np.array(pclst) fig, ax = plt.subplots(1) ax.plot(pclst, prlst) numOfCentroids = 10 k = np.array(random.sample(zip(*np.where(sampleMatrix == 1)), numOfCentroids), dtype=np.float) observation = np.array(zip(*np.where(sampleMatrix==1)), dtype=np.float) # computing K-Means with K = 2 (2 clusters) centroids,_ = kmeans2(observation, k, minit="point") # assign each sample to a cluster idx,_ = vq(observation,centroids) centroidsRow, centroidsCol = centroids.T centroidsRow = map(lambda x: int(round(x)), centroidsRow) centroidsCol = map(lambda x: int(round(x)), centroidsCol) sampleMatrixDummy = sampleMatrix.copy() sampleMatrixDummy[centroidsRow, centroidsCol] = 5 pb(sampleMatrixDummy) ax.plot(centroids[:,1], centroids[:,0], "sm") fig.savefig("/Users/kei/Desktop/saisenka.png")
出力結果:
赤い点が周辺領域の代表点になります。形状の情報をよく表しているのではないかと思います。k-meansは形状の情報削減、圧縮、細線化などに使えそうなことがわかると思います。
昨日はオブジェクトから輪郭を抽出する処理を書きました。今日は、
輪郭を等間隔に間引いたものの形状がどうなるか見てみます。前処理としてオリジナルデータのオブジェクト領域が小さかったのでscipyのzoom関数で2倍に拡大してから処理に入っています。あと、符号化もしてます。等間隔に間引くだけなら符号化する必要はないかもしれませんが、あとで何かに使えるかもしれないので、符号化してみました。
# -*- coding: utf-8 -*- import random import re import numpy as np from numpy import arange import matplotlib.pyplot as plt from scipy.ndimage import zoom def rawObjectToMatrix(ro): ret = [] lines = ro.splitlines() for line in lines: if line.strip()=="": continue ret.append(map(lambda x: 0 if x == " " else 1, line)) whiteRow = [[0 for _ in xrange(len(ret[0]))]] return whiteRow + ret + whiteRow def pb(lst2d): ret = "" for lst in lst2d: lst = map(abs, lst) ret += "".join(map(str, lst)) + "\n" print "" print ret def readMyData(): with open("a.txt", 'r') as fp: text = fp.read() objectRawDataList = re.split(r"\n\n", text) objectRawDataList = filter(lambda x: x != "", objectRawDataList) return map(lambda ro: rawObjectToMatrix(ro), objectRawDataList) lst = readMyData() sampleMatrix = zoom(np.array(lst[5]), 1.5) pb(sampleMatrix) height = sampleMatrix.shape[0] width = sampleMatrix.shape[1] mat = sampleMatrix.copy() for rn, _ in enumerate(mat): for cn, __ in enumerate(_): if mat[rn, cn] == 1 and mat[rn + 1, cn] == 1: mat[rn, cn] = 0 out = mat mat = sampleMatrix.copy() for rn, _ in enumerate(mat): for cn, __ in enumerate(_): if mat[(height - 1) - rn, cn] == 1 and mat[(height - 1) - rn - 1, cn] == 1: mat[(height - 1) - rn, cn] = 0 out |= mat mat = sampleMatrix.copy() for rn, _ in enumerate(mat): for cn, __ in enumerate(_): if mat[rn, cn] == 1 and mat[rn, cn + 1] == 1: mat[rn, cn] = 0 out |= mat mat = sampleMatrix.copy() for rn, _ in enumerate(mat): for cn, __ in enumerate(_): if mat[rn, (width - 1) - cn] == 1 and mat[rn, (width - 1) - cn - 1] == 1: mat[rn, (width - 1) - cn] = 0 out |= mat pb(out) firstPosFlat = out.flatten().argmax() pos = firstPosFlat / width, firstPosFlat % width startPos = pos code = [] while True: if out[pos[0] - 1, pos[1] ] == 1: out[pos[0] - 1, pos[1] ] = 2 pos = pos[0] - 1, pos[1] code.append((-1, 0)) elif out[pos[0] - 1, pos[1] + 1] == 1: out[pos[0] - 1, pos[1] + 1] = 2 pos = pos[0] - 1, pos[1] + 1 code.append((-1, 1)) elif out[pos[0] , pos[1] + 1] == 1: out[pos[0] , pos[1] + 1] = 2 pos = pos[0] , pos[1] + 1 code.append((0, 1)) elif out[pos[0] + 1, pos[1] + 1] == 1: out[pos[0] + 1, pos[1] + 1] = 2 pos = pos[0] + 1, pos[1] + 1 code.append((1, 1)) elif out[pos[0] + 1, pos[1] ] == 1: out[pos[0] + 1, pos[1] ] = 2 pos = pos[0] + 1, pos[1] code.append((1, 0)) elif out[pos[0] + 1, pos[1] - 1] == 1: out[pos[0] + 1, pos[1] - 1] = 2 pos = pos[0] + 1, pos[1] - 1 code.append((1, -1)) elif out[pos[0] , pos[1] - 1] == 1: out[pos[0] , pos[1] - 1] = 2 pos = pos[0] , pos[1] - 1 code.append((0, -1)) elif out[pos[0] - 1, pos[1] - 1] == 1: out[pos[0] - 1, pos[1] - 1] = 2 pos = pos[0] - 1, pos[1] - 1 code.append((-1, -1)) if pos == startPos: break pb(out) mabiki = [1, 2, 4, 8, 16, 32] for m in mabiki: pr = 0 pc = 0 p = [] for n, c in enumerate(code): dr, dc = c pr += dr pc += dc if n % m == 0: p.append((pr, pc)) x = map(lambda x: x[1], p) y = map(lambda x: x[0], p) x += [0] y += [0] fig, ax = plt.subplots(1) ax.plot(x, y) fig.savefig("/Users/kei/Desktop/mabiki%d.png"%m)
出力(上から順に、オリジナル, 2, 4, 8, 16, 32ステップごとにサンプルをとった場合の集合):
いきなりですが、輪郭抽出をPythonで実装しました。
ここでいう輪郭抽出とは画像にフィルタを適用する話ではありません。二値画像のオブジェクトの輪郭を綺麗に抽出したいのです。
すでに、このアルゴリズムがあるかは知りませんが、アルゴリズムのイメージだけ説明すると、上下左右の4方向から光をてらして光が当たる部分だけ取り出すという感じです。実際にはひたすら「連続する(1,1)を消していく」を4方向分行うだけです。
境界値処理は行っていません。以下のように入力データを加工したので、境界値の問題は発生しませんでした。
00000 < はじめと終わりの行データが非オブジェクト(0)である
00100 < 左端と右端の列データが非オブジェクト(0)である
01110
00100
00000
以下に、冗長だがよみやすいソースコードを記載していることを望みます。
# -*- coding: utf-8 -*- import random import re import numpy as np from numpy import arange def rawObjectToMatrix(ro): ret = [] lines = ro.splitlines() for line in lines: if line.strip()=="": continue ret.append(map(lambda x: 0 if x == " " else 1, line)) whiteRow = [[0 for _ in xrange(len(ret[0]))]] return whiteRow + ret + whiteRow def pb(lst2d): ret = "" for lst in lst2d: lst = map(abs, lst) ret += "".join(map(str, lst)) + "\n" print "" print ret def readMyData(): with open("a.txt", 'r') as fp: text = fp.read() objectRawDataList = re.split(r"\n\n", text) objectRawDataList = filter(lambda x: x != "", objectRawDataList) return map(lambda ro: rawObjectToMatrix(ro), objectRawDataList) lst = readMyData() sampleMatrix = np.array(lst[5]) pb(sampleMatrix) height = sampleMatrix.shape[0] width = sampleMatrix.shape[1] mat = sampleMatrix.copy() for rn, _ in enumerate(mat): for cn, __ in enumerate(_): if mat[rn, cn] == 1 and mat[rn + 1, cn] == 1: mat[rn, cn] = 0 out = mat mat = sampleMatrix.copy() for rn, _ in enumerate(mat): for cn, __ in enumerate(_): if mat[(height - 1) - rn, cn] == 1 and mat[(height - 1) - rn - 1, cn] == 1: mat[(height - 1) - rn, cn] = 0 out |= mat mat = sampleMatrix.copy() for rn, _ in enumerate(mat): for cn, __ in enumerate(_): if mat[rn, cn] == 1 and mat[rn, cn + 1] == 1: mat[rn, cn] = 0 out |= mat mat = sampleMatrix.copy() for rn, _ in enumerate(mat): for cn, __ in enumerate(_): if mat[rn, (width - 1) - cn] == 1 and mat[rn, (width - 1) - cn - 1] == 1: mat[rn, (width - 1) - cn] = 0 out |= mat pb(out)
a.txtのデータはこちらになります。
@@ @@@@ @@@@@@ @@@@@@@ @@@@@@ @@@@@@ @@@@ @@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@ @@@@@ @@@ @@@@@@@@ @@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@ @@@@ @@@@@@@ @@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@ @@@@@@@@@ @@@@@@@@ @@@@@@@@@@ @@@@@@@@ @@@@@@@@@@@@ @@@@@@@@ @@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@ @@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@ @@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@ @@@@ @@@@@ @@@@@@ @@@@@@ @@@@@@ @@ @@@@@@@ @@@@ @@@@@@@ @@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@ @@@@@@ @@@@@@@
実行結果
0000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000011111111100000000000000 0000000000000000000000000000111111111111111100000000000 0000000000000000000000000111111111111111111111100000000 0000000000000000000000011111111111000000111111111000000 0000000000000000000001111111110000000000001111111100000 0000000000000000000001111000000000000000000001111100000 0000000000000000000000000000000000000000000001111110000 0000000000000000000000000000000000000000000001111110000 0000000000000000000000000000000000000000000001111110000 0000000000000000000110000000000000000000000011111110000 0000000000000000001111000000000000000000000111111100000 0000000000000000001111110000000000000000001111111100000 0000000000000000000111111110000000000111111111111000000 0000000000000000000001111111111111111111111111110000000 0000000000000000000000001111111111111111111111000000000 0000000000000000000000000000011111101111111000000000000 0000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000011111111100000000000000 0000000000000000000000000000111100000000011100000000000 0000000000000000000000000111000000111111000011100000000 0000000000000000000000011000001111000000110000011000000 0000000000000000000001100111110000000000001110000100000 0000000000000000000001111000000000000000000001000100000 0000000000000000000000000000000000000000000001000010000 0000000000000000000000000000000000000000000001000010000 0000000000000000000000000000000000000000000001000010000 0000000000000000000110000000000000000000000010000010000 0000000000000000001001000000000000000000000100000100000 0000000000000000001000110000000000000000001000000100000 0000000000000000000110001110000000000111110000001000000 0000000000000000000001110001111111111000000000110000000 0000000000000000000000001111100000010000000111000000000 0000000000000000000000000000011111101111111000000000000 0000000000000000000000000000000000000000000000000000000
これを見ると、ガタガタじゃないかと思われるが、人間の目には"1"の領域の輪郭がしっかり把握できるのは、"1"という文字を符号としてみているのではなく、画像としてみているー数百ドットではなく数万ドットやそれ以上として認識されているーからだと思います。