つれづれなる備忘録

日々の発見をあるがままに綴る

Texによる文書作成39 ~電気回路の作図9

今回はTeXを用いた電気回路図の作成のうちOPアンプ、スイッチの表示について紹介したい。

atatat.hatenablog.com

1. OPアンプ

OPアンプ単体を表示するにはnode[op amp]とする。

\begin{figure}
\begin{center}
\begin{circuitikz}
\draw (0,0) node[op amp] {};
\end{circuitikz}
\caption{OPアンプ}
\end{center}
\end{figure}

"OPアンプ"
OPアンプ

OPアンプの入力、出力にラベルを表示するには、OPアンプの入力、出力の座標に表示したい文字を挿入する。 OPアンプの入力の座標はプラス側:(,+),マイナス側:(.-), 出力側:(.out)で取得できる。

例えば、OPアンプの名称をopampとして、(opamp.+) node[left] {$v_+$}とすれば入力左側にv+のラベルが表示される。

\begin{figure}
\begin{center}
\begin{circuitikz} 
\draw (0,0) node[op amp] (opamp) {}
(opamp.+) node[left] {$v_+$}
(opamp.-) node[left] {$v_-$}
(opamp.out) node[right] {$v_o$};
\end{circuitikz}
\caption{OPアンプ,ラベル}
\end{center}
\end{figure}

OPアンプ,ラベル
OPアンプ,ラベル

OPアンプの電源供給を表示するには、座標(.up)(.down)を用いる。例えば+電圧と接続したければ、(.up)から適当な距離(0,0.5)をインクリメントして、node[vcc]に接続する。

\begin{figure}
\begin{center}
\begin{circuitikz} \draw
(0,0) node[op amp] (opamp) {}
(opamp.up) --++(0,0.5) node[vcc]{12\,\textnormal{V}}
(opamp.down) --++(0,-0.5) node[ground]{};
\end{circuitikz}
\caption{OPアンプ, +/グランド接続}
\end{center}
\end{figure}

"OPアンプ電源接続"
OPアンプ電源接続

\begin{figure}
\begin{center}
\begin{circuitikz} \draw
(0,0) node[op amp] (opamp) {}
(opamp.+) node[left] {$v_+$}
(opamp.-) node[left] {$v_-$}
(opamp.out) node[right] {$v_o$}
(opamp.up) --++(0,0.5) node[vcc]{5\,\textnormal{V}}
(opamp.down) --++(0,-0.5) node[vee]{-5\,\textnormal{V}};
\end{circuitikz}
\caption{OPアンプ ±接続}
\end{center}
\end{figure}

"OPアンプ、±電源"
OPアンプ、±電源

2. OPアンプ応用回路

OPアンプの応用として非反転増幅回路を表示する。まずデフォルトのOPアンプの入力は+が下、-が上になっているが、noinv input upをオプション指定することで上下入れ替えることができる。-入力側、線を垂直方向に抵抗R1、グランドを描画するには(opamp.-) -- ++(0,-1) to [R=$R_1$] ++ (0,-1.5) node[ground]{}となるが、抵抗の接続点座標を後で再利用するためcoordinate(BR)として座標の固有名を与えておく。 次にR1の接続点から抵抗R2とOPアンプ出力を接続する。R1の接続点とOPアンプの出力座標の交点を指定する便利な方法として(BR -| opamp.out)とするだけでよい。 R1の接続点から抵抗R2、OPアンプの出力座標の交点、OPアンプの出力座標を描画は (BR) to [R=$R_2$] (BR -| opamp.out) -- (opamp.out) -- ++(1,0)となる。最後の++(1,0)はOPアンプの出力を少し伸ばすために入れた。

\begin{figure}
\begin{center}
\begin{circuitikz}
\draw (0,0) node[op amp, noinv input up] (opamp){}
(opamp.-) -- ++(0,-1) coordinate(BR) to [R=$R_1$] ++(0,-1.5) node[ground]{}
(BR) to [R=$R_2$]  (BR -| opamp.out) -- (opamp.out) -- ++(1,0) ;
\end{circuitikz}
\caption{非反転増幅回路}
\end{center}
\end{figure}

"非反転増幅回路"
非反転増幅回路

3. スイッチ

 スイッチは開閉の矢印つきのものは閉じる方向のものでspstまたはcspst、開く方向のものはospstを用いる。 また矢印なしの場合はostを用いる。

\begin{figure}
\begin{center}
\begin{circuitikz}
\draw (0,0) [spst] to ++(1,0);
\draw (0,-1) [cspst] to ++(1,0);
\draw (0,-2) [ospst] to ++(1,0);
\draw (0,-3) [nos] to ++(1,0);
\end{circuitikz}
\caption{スイッチ}
\end{center}
\end{figure}

"スイッチ"
スイッチ

2分岐スイッチはnode[spdt]を用いる。それぞれラベルを付けるには、入力側は.in, 分岐出力側は,out 1, .out 2で座標を取得することができる。

\begin{figure}
\begin{center}
\begin{circuitikz}
\draw (0,0) node[spdt](Sw) {}
(Sw.in) node[left] {in} 
(Sw.out 1) node[right] {out 1}
(Sw.out 2) node[right] {out 2};
\end{circuitikz}
\caption{分岐スイッチ}
\end{center}
\end{figure}

"分岐スイッチ"
分岐スイッチ

スイッチにノード(白丸)を表示したい場合、Cute Switch: coswを利用する。デフォルトはスイッチ部分の線が太いので\ctikzset{bipoles/cuteswitch/thickness=0.2}としてCute Switchの線幅を変更することで、回路図でよくみかける形式になる。

\begin{figure}
\begin{center}
\begin{circuitikz}
\draw (0,0) [cosw] to ++(2,0);
\ctikzset{bipoles/cuteswitch/thickness=0.2}
\draw (0,-1) [cosw] to ++(2,0);
\end{circuitikz}
\caption{ノード付きスイッチ}
\end{center}
\end{figure}

"ノード付きスイッチ"
ノード付きスイッチ

4 まとめ

今回はOPアンプの表示とラベルの付け方、入出力の座標取得、またスイッチの表示について紹介した。

ブログデザイン備忘録 ~ 疑似クラスNOT, -of-type

今回は以下の疑似クラスNOT, last-of-typeをHTMLの表に適用した例を紹介したい。

coliss.com

CSS疑似クラス:NOTは、CSSのスタイルの適用を除外し、:last-of-typeは最後の要素を指定することができる。 上の記事はリストに適用しているが、表に適用することもできる。以下のようにHTMLで簡単な3x3の表を作成する。

<table>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
    <tr>
      <td>4</td>
      <td>5</td>
      <td>6</td>
    </tr>
    <tr>
      <td>7</td>
      <td>8</td>
      <td>9</td>
    </tr>
 </table>

太字・赤枠をCSSで書式設定する。まず:not(:last-of-type)をタグtdに対して適用すると、右端の列(最後の列)のみスタイルが適用されない。

td:not(:last-of-type)
{
  font-weight:bold;
  border: solid 2px red;
}

tdに適用
tdに適用

次にタグtrについて適用すると、最下段の行のみスタイルの適用が除外される。

tr:not(:last-of-type)
{
  font-weight:bold;
  border: solid 2px red;
}

"trに適用"
trに適用

エクセルで作成する表のように表全体に罫線を入れ、最初の行だけ強調して太字、赤線にするには、 まずtdを指定してborder: solid 1px;として全体に罫線を適用し、次にtr:first-of-typeとすると最初の行のみに太字・赤線が適用される。 :NOTを除けば逆に書式が適用され、また:first-of-typeは最初の要素を指定することができる。

td{
  border: solid 1px;
}
tr:first-of-type
{
  font-weight:bold;
  border: solid 2px red;
}

"1st-of-typeを使用"
1st-of-typeを使用

実際の動作を以下のCODE Penで確認できる。

See the Pen NOT by ATATAT (@atatat) on CodePen.

OpenCVの使い方28 ~ 輪郭抽出応用

今回は前回までのOpenCVを用いた輪郭抽出の応用例として赤血球のカウント、サイズ分布について紹介したい。

atatat.hatenablog.com

1. 画像読み込みと2値化処理

 今回は適当な赤血球の顕微鏡画像を用意してGoogle Corabで読み込んだものを使用する。

Google Colabでのサンプル画像読み込み

import cv2
import numpy as np
from matplotlib import pyplot as plt
from google.colab import files
uploaded_file = files.upload()
uploaded_file_name = next(iter(uploaded_file))
orig = cv2.imread(uploaded_file_name)

もとのカラー画像をsrc, 2値化処理のためのグレー画像をgrayとして格納する。

src = cv2.cvtColor(orig, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY)
plt.imshow(src)
plt.title('Original')

赤血球画像
赤血球画像

plt.imshow(gray,cmap='gray')
plt.title('Gray')

赤血球グレー画像
赤血球グレー画像

次に2値化処理をするが、暗い部分が赤血球になり、今回は暗い部分を白にするためcv2.thresholdのオプションをcv2.THRESH_BINARY_INVと指定する。

ret,thresh = cv2.threshold(gray,110,255,cv2.THRESH_BINARY_INV)
plt.imshow(thresh,cmap='gray',vmin=0,vmax=255)
plt.title('Binary')

バイナリ画像
バイナリ画像

閾値110としたが、数値を変えると輪郭抽出の結果が異なってくる。

2. 赤血球輪郭の抽出

 2値化したバイナリ画像からcv2.findContours()を用いて輪郭を抽出する際、赤血球内部の輪郭は不要のため、外部のみの輪郭を抽出するcv2.RETR_EXTERNALを指定する。

contours, hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnt_img = np.zeros_like(gray, dtype=np.uint8) 
cnt_img_tree = cv2.drawContours(cnt_img, contours, -1, 255, 2)
plt.imshow(cnt_img_tree,cmap='gray',vmin=0,vmax=100)

輪郭画像
輪郭画像

抽出した輪郭がバイナリ画像とあっているか確認するため、バイナリ画像の強度を下げて輪郭画像と重ね合わせる。

th2=(thresh/10)
plt.imshow(cnt_img_tree+th2,cmap='gray',vmin=0,vmax=100)

おおざっぱに輪郭抽出が正しいことが確認できる。

輪郭・バイナリ重ね合わせ
輪郭・バイナリ重ね合わせ

3. サイズ分布

抽出した輪郭の数から赤血球をカウントするが、画像と比較してかなり多いことがわかる。

n=len(contours)
n
>624

そこで輪郭の面積をcv2.contourArea()で取得し、ヒストグラムをプロットしてサイズ分布を確認する。

ind=[]
areas=[]
for i in np.arange(n):
  cnt=contours[i]
  area = cv2.contourArea(cnt)
  ind.append(i)
  areas.append(area)

plt.hist(areas,bins=25)

明らかに面積が小さいものが偏って含まれており、画像のノイズと考えられる。また大きいサイズのものは赤血球が2つ以上重なっているものと考えられる。

輪郭サイズ分布
輪郭サイズ分布

4. イレギュラーな輪郭の除外

イレギュラーなものを除外した赤血球のサイズ分布を調べる。ノイズと赤血球が重なったものを除外するためサイズを3500以上8500以下のものに限定する。

ind=[]
areas=[]
for i in np.arange(n):
  cnt=contours[i]
  area = cv2.contourArea(cnt)
  if area > 3500 and area<8500:
    ind.append(i)
    areas.append(area)
n2=np.size(ind)
n2
>130

サイズを限定することで抽出した輪郭数が130まで減少した。

plt.hist(areas)

サイズの分布は6000強をピークとする分布になっていることがわかる。

選択サイズ分布
選択サイズ分布

サイズ制限した場合の輪郭とバイナリ画像を重ね合わせると、概ね2つ以上重なっているものは除外できていることが確認できる。また画像端にある赤血球の輪郭も除外されていることがわかる。またカウントとしては重なっているものを除外したため精度はよいとは言えないが、例えばサイズ制限で通常の2倍程度のものを抽出するなどの工夫の余地はある。

img2 = np.zeros_like(thresh, dtype=np.uint8) 
for i in np.arange(n2):
  cnt_img2 = np.zeros_like(thresh, dtype=np.uint8) 
  cnt_img2 = cv2.drawContours(cnt_img2, contours,ind[i], 255, 2)
  img2=img2+cnt_img2

plt.imshow(img2+th2,cmap='gray',vmin=0,vmax=100)

"選択輪郭・バイナリ重ね合わせ"
選択輪郭・バイナリ重ね合わせ

5. まとめ

 今回は輪郭抽出の応用として実際の赤血球の画像からカウントとサイズ分布を調べる方法について紹介した。