最も使いやすい言語の 1 つとして、Python には多数のサードパーティ ライブラリがあり、これらのサードパーティ ライブラリの存在により、多くの人は面倒なコード操作を無視してビジネス ロジックと数学的論理に集中することができます。 -party library もその 1 つです。
1. サードパーティライブラリのインストールと簡単な使用
インストール
単純な pip のインストールで十分ですが、opencv ライブラリの使用には行列演算が含まれることが多いため、numpy はそれと同族のものとみなすことができます。
pip install opencv-python
インストール後は、画像を開いたり、ビデオを開いたりするだけで済みます。簡単な実験をしてみましょう。
写真を読む
import cv2
# 读取图像,第一种是正常读取,第二种是读取灰度图像
img = cv2.imread(r"D:\img\among.png")
gray = cv2.imread(r"D:\img\among.png", 0)
# 显示图像
cv2.imshow("colorful", img)
cv2.imshow("gray", gray)
# 不再等待键盘输入事件,直接显示
cv2.waitKey(0)
# 关闭所有显示窗口
cv2.destroyAllWindows()
表示効果は次のとおりです:
ビデオを読み取って再生します。
import cv2
# 读取视频
video = cv2.VideoCapture('badapple_color.mp4')
# 获取视频对象的帧数
fps = video.get(cv2.CAP_PROP_FPS)
# 设定循环条件
while(video.isOpened()):
_, frame = video.read()
cv2.imshow("video", frame)
# 设置退出条件是输入'q'
if cv2.waitKey(int(fps)) in [ord('q'), 27]:
break
cv2.destroyAllWindows()
video.release()
注: ここでの再生には、通常のプレーヤーのような進行状況バーと音声がありません。これは、読み取られたビデオのすべてのフレームがあり、ループで再生され、音声は読み取られず、終了条件が設定されるためです。 qを入力します。
カメラ録画を取得してビデオを保存する
import cv2
video = cv2.VideoCapture(0)
while(True):
# 获取一帧
_, frame = video.read()
# 将这帧转换为灰度图
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('frame', gray)
if cv2.waitKey(1) == ord('q'):
break
video.release()
次に、カメラが受け取るすべてのフレームを継続的に出力します。読み取られるのはグレースケールであるため、ここでもグレースケールです。変更せずに通常どおり出力させることができます。つまり、 cvtColor の変換ステートメントをコメントアウトします。ちなみに、 imshow 出力オブジェクトを元のフレーム (読み取りフレーム) に戻すと、カラー マップを取得できます。
ビデオを保存するために小さな変更を加えます。
import cv2
video = cv2.VideoCapture(0)
# 定义编码方式并创建VideoWriter对象
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
outfile = cv2.VideoWriter('res.mp4', fourcc, 25., (640, 480))
while(video.isOpened()):
flag, frame = video.read()
if flag:
outfile.write(frame) # 写入文件
cv2.imshow('frame', frame)
if cv2.waitKey(1) == ord('q'):
break
else:
break
video.release()
次のように:
2. イメージの基礎
コンピュータ内の画像は色の付いた小さな四角形で構成されており、この小さな四角形がピクセルと呼ばれる基本処理単位となります。そのサイズはコンピュータの解像度によって異なり、解像度が高くなるほどピクセルは小さくなります。ピクセル値が 0 と 1 のみである単純な 2 値画像は、黒と白の 2 つの色を識別するために使用されます。ここでのさらなるグレースケール画像は、画像をより鮮明にするために黒と白を調整することです。黒と白の値は 0 ~ 255 で格納され、0 は純粋な黒、1 は純粋な白、中央の白と黒のグラデーションはより繊細で、さらにカラー画像であり、色の構成を表します基本的に、異なる比率の三原色で構成されており、カラー画像は三原色の値を表す 3 つの値、つまりカラー画像のピクセル、カラーは 3 つの値で構成され、このモードは RGB カラー スペースとも呼ばれます。たとえば、[0, 0, 0] は純黒を表し、[255, 255, 255] は純白を表し、[255, 0, 0]、[0, 255, 0]、[0, 0, 255] は純白を表します。それぞれ赤、緑、青の三原色であるため、RGB 色空間は 3 チャネルとも呼ばれますが、opencv では 3 チャネルの順序は BGR の逆順になります。カラー画像は 3 つのチャネルからなるマトリックスで構成されているため、カラー画像を処理する際の連携のために Numpy が導入されることがよくあります。画像処理も数学の問題です。
簡単な例:
import cv2
import numpy as np
# 黑白图
b = np.zeros((100, 100), dtype=np.uint8)
w = np.zeors((100, 100), dtype=np.uint8)
w[:100, :100] = 255
print(b, w, sep="\n\n")
cv2.imshow("black", b)
cv2.imgshow("white", w)
cv2.waitKey()
# 三个三原色图片
r = np.zeros((300, 300, 3),dtype=np.uint8)
g = np.zeros((300, 300, 3),dtype=np.uint8)
b = np.zeros((300, 300, 3),dtype=np.uint8)
r[:,:, 2] = 255
g[:,:, 1] = 255
b[:,:, 0] = 255
cv2.imshow("red", r)
cv2.imshow("green", g)
cv2.imshow("blue", b)
cv2.waitKey()
# 包含三原色的图片
img = np.zeros((300, 300, 3), dtype=np.uint8)
img[:, 0:100, 2] = 255
img[:, 100:200, 1] = 255
img[:, 200:300, 0] = 255
cv2.imshow("RGB", img)
# 红橙黄绿蓝靛紫
img = np.zeros((300, 700, 3), dtype=np.uint8)
# 红
img[:,0:100,2] = 255
# 橙
img[:,100:200,2] = 255
img[:,100:200,1] = 97
# 黄
img[:,200:300,1] = 255
img[:,200:300,2] = 255
# 绿
img[:,300:400,1] = 255
# 蓝
img[:,400:500,0] = 255
# 靛
img[:,500:600,0] = 84
img[:,500:600,1] = 46
img[:,500:600,2] = 8
# 紫
img[:,600:700,0] = 240
img[:,600:700,1] = 32
img[:,600:700,2] = 160
# 输出
cv2.imshow("seven", img)
cv2.waitKey()
出力が表示されない、それだけです。実際、カラー比較テーブルを作成したい場合は、1 つのチャネルを 1 つのチャネルごとに繰り返し、シーケンス全体を繰り返して出力することもできます。これにより、カラー テーブルが作成されますが、実際のカラー テーブルを参照する必要があります。値を準備する場合は名前を指定します。
ランダムグラフ
全体のランダムなグレースケール画像は、過去にテレビ信号がなかった状態です。
import cv2
import numpy as np
img = np.random.randint(0, 256, size=[300, 300], dtype=np.uint8)
cv2.imshow("老花", img)
cv2.waitKey()
img = np.random.randint(0, 256, size=[300, 300], dtype=np.uint8)
cv2.imshow("彩色老花", img)
cv2.waitKey()
|
RGB カラーマップには 3 つのチャネルがあり、opencv はチャネル分割の実装を提供することは上で説明しました。
import cv2
import numpy as np
img = cv2.imread(r"D:\img\among.png")
# b, g, r = cv2.split(img) 等同
cv2.imshow("0", img[:,:,0])
cv2.imshow("1", img[:,:,1])
cv2.imshow("2", img[:,:,2])
# 同样的拆分功能
b, g, r = cv2.split(img)
# 合并成原图
img_mer = cv2.merge([b, g, r])
cv2.waitKey()
効果:
3 つの属性値
- 形状 img.shape は、img シーケンスの長さ、幅、深さを示します。
- size 、 img.size は、行 x 列 x チャネルのピクセル数を示します。
- dtype、画像データ型
3. 色空間と変換
色を表現する方式である色空間は、一般的な色空間はRGB色空間ですが、opencvでは逆BGRチャンネルになります。さらに、8 ビットのグレースケール画像である GRAY、XYZ 色空間、YCrCb 色空間、HSV 色空間、HLS 色空間、および Bayer 色空間があります。。。。。。さまざまなニーズに応じて、必要に応じて色空間を変換することもできますが、ここではその特性と相互変換について学びます。
灰色の色空間
8 ビット グレースケール イメージ。8 ビット バイナリ値の範囲は 0 ~ 255 に対応します。これは 256 グレー レベルを意味します。0 は純粋な黒を意味し、255 は純粋な白を意味します。中間の値は純粋な黒から純粋な白へのグラデーションです。 、つまりグレーのSpendです。opencv では、RGB 色空間を GRAY などのグレースケール色空間に変換します。その変換式は次のとおりです。
グレー = 0.299 * R + 0.587 * G + 0.114 * B グレー = 0.299 * R + 0.587 * G + 0.114 * Bグレー_ _ _=0.299∗R+0.587∗G+0.114∗B
Gray のグレー スケールを RGB 色空間に変換するのは比較的簡単です。RGB 3 チャネルの値はそのまま Gray の値になります。つまり、
$$R = Gray\G = Gray\B = Gray\$$ となります。
GRAY と BGR の変換については、実は opencv が読み込むことで実現できるのですが、当面は応用が全く分かりませんので、主にサンプルで遊んでみることにします。
>>> import cv2
>>> import numpy as np
>>>
>>>
>>> mong = cv2.imread("among.png")
>>> gray = cv2.cvtColor(mong, cv2.COLOR_BGR2GRAY)
>>> cv2.imshow("source", mong)
>>> cv2.imshow("gray", gray)
>>> bgr_img = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
>>> cv2.imshow("change again", bgr_img)
>>> cv2.waitKey()
-1
>>>
>>>
>>> img = np.random.randint(0, 256, size=[2,3], dtype=np.uint8)
>>> res = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
>>> res_change = cv2.cvtColor(res, cv2.COLOR_BGR2GRAY)
>>> img
array([[ 48, 27, 228],
[ 94, 144, 234]], dtype=uint8)
>>> res
array([[[ 48, 48, 48],
[ 27, 27, 27],
[228, 228, 228]],
[[ 94, 94, 94],
[144, 144, 144],
[234, 234, 234]]], dtype=uint8)
>>> res_change
array([[ 48, 27, 228],
[ 94, 144, 234]], dtype=uint8)
>>>
プレーオフになり、元の色に戻りませんでした。
YCrCb色空間
人間の視覚システムの場合、色の知覚は光の明るさの知覚よりも低く、RGB 色空間は色に焦点を当てているため、明るさの指標が欠落しているため、YCrCb 色空間が存在します。この色空間では、Y は光源の明るさを表し、Cr は赤色成分を表し、Cb は青色成分を表します。RGB を YCrCb に変換するための変換式は次のとおりです。
Y = 0.299 ∗ R + 0.587 ∗ G + 0.114 ∗ BC r = ( R − Y ) × 0.713 + デルタ C b = ( B − Y ) × 0.564 + デルタ Y = 0.299* R + 0.587*G + 0.114*B \\ Cr = (RY)\times0.713+delta\\Cb=(BY)\times0.564+deltaY=0.299∗R+0.587∗G+0.114∗BC r=( R−や)×0.713+デルタ_ _ _Cb _=( B−や)×0.564+デルタのデルタ値には
、さまざまなデジタル画像に対応するさまざまな値があります。
デルタ値 | 画像の数字 |
---|---|
128 | 8ビット |
32768 | 16ビット |
0.5 | 単精度画像 |
次に、YCrCb から RGB に変換する式は次のとおりです。 |
R = Y + 1.403 ⋅ ( C r − デルタ ) G = Y − 0.714 ⋅ ( cr − デルタ ) − 0.344 ⋅ ( C b − デルタ ) B = Y + 1.773 ⋅ ( C b − デルタ ) R=Y+1.403\ cdot(Cr-デルタ)\\G=Y-0.714\cdot(cr-デルタ)-0.344\cdot(Cb-デルタ)\\B=Y+1.773\cdot(Cb-デルタ)R=Y+1.403⋅( C r−デルタ) _ _ _G=Y−0.714⋅( cr−デルタ) _ _ _−0.344⋅( Cb _−デルタ) _ _ _B=Y+1.773⋅( Cb _−デルタ) _ _ _
YUVとも呼ばれ、Yは明るさ、UとVは彩度を表し、肌色の検出においてはHSVよりも優れています。
HSV色空間
視覚の色モデルと言われていますが、この色空間には色相、彩度、明度の3要素があり、色相は光の色、彩度は色の濃さ、明度は明るさです。人間の目で認識される光。
- 色相 H、赤、黄、緑、青、赤の 6 色は 360 度の円に対応します (だからこそ、これらの概念を作成する人は人々を困惑させるのが好きなのです)。
- 彩度 S、比率、つまり小数は 0 から 1 の範囲で、色に対する色の最大純度の比率を示します。彩度 0 はグレー、最大 1 は色そのものです。
- 色の明るさである明るさ V の値の範囲も [0, 1] です。
RGB を HSV に変換する式は次のとおりです。
V = max ( R , G , B ) 明るさ: S = { V − min ( R , G , B ) 、V ≠ 0 0 、その他の色相: H = { 60 ( G − B ) v − min ( R , G , B ) 、V = R 120 + 60 ( B − G ) V − min ( R , G , B ) 、V = G 240 + 60 ( R − G ) V − min ( R , G , B ) 、 V = B 色相: H = { H + 360 、 H < 0 H 、その他 V = max(R, G, B)\\明るさ: S= \begin{cases} V- min(R , G, B), &V\ne0\\ 0,& その他 \end{cases}\\hue: H= \begin{cases} \cfrac{60(GB)}{v-min(R, G , B) },&V=R\\ 120+\cfrac{60(BG)}{V-min(R, G, B)}, &V=G\\ 240+\cfrac{60(RG)}{V -min( R, G, B)}, &V=B \end{cases}\\Hue: H= \begin{cases} H+360,&H<0\\ H,&other\end{cases}V=マックス( R , _G 、B )明るさ:S={
V−分( R ,G 、B ) 、0、V=0他の色相: H=⎩
⎨
⎧v−分( R ,G 、B )60 ( G)−B )、120+V−分( R ,G 、B )60 ( B)−G )、240+V−分( R ,G 、B )60 ( R)−G )、V=RV=GV=B色相: H={
H+360 、H、H<0他の
上記はRGBからHSVへの変換式です。本当に面倒です。原理を知っておくとデバッグの便宜にもなります。実はopencvの内部関数で実現できます。意味だけ気にしておけば大丈夫です。以上の3つの重要な要素です。
>>> rgb_b = np.ones((2, 3, 3), dtype=np.uint8)
>>> rgb_g = np.ones((2, 3, 3), dtype=np.uint8)
>>> rgb_r = np.ones((2, 3, 3), dtype=np.uint8)
>>> rgb_b[:, :, 0], rgb_g[:, :, 1], rgb_r[:, :, 2] = 255, 255, 255
>>> hsv_b = cv2.cvtColor(rgb_b, cv2.COLOR_BGR2HSV)
>>> hsv_g = cv2.cvtColor(rgb_g, cv2.COLOR_BGR2HSV)
>>> hsv_r = cv2.cvtColor(rgb_r, cv2.COLOR_BGR2HSV)
>>> rgb_b
array([[[255, 1, 1],
[255, 1, 1],
[255, 1, 1]],
[[255, 1, 1],
[255, 1, 1],
[255, 1, 1]]], dtype=uint8)
>>> hsv_b
array([[[120, 254, 255],
[120, 254, 255],
[120, 254, 255]],
[[120, 254, 255],
[120, 254, 255],
[120, 254, 255]]], dtype=uint8)
>>> rgb_g
array([[[ 1, 255, 1],
[ 1, 255, 1],
[ 1, 255, 1]],
[[ 1, 255, 1],
[ 1, 255, 1],
[ 1, 255, 1]]], dtype=uint8)
>>> hsv_g
array([[[ 60, 254, 255],
[ 60, 254, 255],
[ 60, 254, 255]],
[[ 60, 254, 255],
[ 60, 254, 255],
[ 60, 254, 255]]], dtype=uint8)
>>> rgb_r
array([[[ 1, 1, 255],
[ 1, 1, 255],
[ 1, 1, 255]],
[[ 1, 1, 255],
[ 1, 1, 255],
[ 1, 1, 255]]], dtype=uint8)
>>> hsv_r
array([[[ 0, 254, 255],
[ 0, 254, 255],
[ 0, 254, 255]],
[[ 0, 254, 255],
[ 0, 254, 255],
[ 0, 254, 255]]], dtype=uint8)
>>>
HLS色空間
HSV色空間と似ていますが、HLS色空間の3要素は色相H、明度L、彩度Sです。本文ではこのように説明されていますが、何が違うのでしょうか?明るさを表す言葉は?無言。
型変換関数
上記の各種色空間とRGB色空間間の変換は、opencvが提供する型変換関数で実現できます。
dst = cv2.cvtColor(src, code[, dstCn])
異なる型変換の場合、異なるコード パラメーター値が渡されます。dstCn はターゲット イメージのチャネル番号で、デフォルトでは 0 です。チャネル番号は元のイメージとコードによって自動的に決定されます。
コード値 | 分析する |
---|---|
cv2.COLOR_BGR2RGB | opencvでBGR型をRGB型に変換する |
cv2.COLOR_RGB2BGR | opencvでRGBタイプをBGRタイプに変換する |
cv2.COLOR_BGR2GRAY | BGRからグレーへ |
cv2.COLOR_GRAY2BGR | グレーからBGR |
cv2.COLOR_BGR2XYZ | BGR発XYZ行き |
cv2.COLOR_XYZ2BGR | XYZ発BGR行き |
cv2.COLOR_BGR2YCrCb | BGR から YCrCb まで |
cv2.COLOR_YCrCb2BGR | YCrCb から BGR |
cv2.COLOR_BGR2HSV | BGR発HSV行き |
cv2.COLOR_HSV2BGR | HSV発BGR行き |
cv2.COLOR_BGR2HLS | BGR発HLS行きフライト |
cv2.COLOR_BayerBG2BGR | アンチモザイク、Bayer の BG モードにも対応 |
上記のパラメータではRGBとBGRの変換がありますが、opencvではチャンネルの順番がBGRになるのが一般的ですが、その逆になりますが、RGBの変換はどうなるのでしょうか?実際の操作の結果によると、B チャネルと R チャネルの値は互いに置き換えられます。つまり、2 つのチャネルが切り替わりますが、opencv レンダリング画像は依然として同じチャネル順序であるため、画像が表示されると色が変わります。
>>> import cv2
>>> import numpy as np
>>>
>>>
>>> img = np.random.randint(0, 256, size=(2, 3, 3), dtype=np.uint8)
>>> rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
>>> img
array([[[ 69, 184, 11],
[193, 4, 194],
[239, 139, 146]],
[[188, 30, 44],
[ 60, 145, 133],
[ 46, 181, 139]]], dtype=uint8)
>>> rgb_img
array([[[ 11, 184, 69],
[194, 4, 193],
[146, 139, 239]],
[[ 44, 30, 188],
[133, 145, 60],
[139, 181, 46]]], dtype=uint8)
>>> mong = cv2.imread("among.png")
>>> rgb_mong = cv2.cvtColor(mong, cv2.COLOR_BGR2RGB)
>>> cv2.imshow("source", mong)
>>> cv2.imshow("rgb_res", rgb_mong)
>>> cv2.waitKey()
-1
上のマトリックスの変更と下の図から、内部の変更とマクロの変更がはっきりとわかります。2 つのチャネルが置き換えられても、opencv のデフォルトのピクチャのチャネル順序や、 imshow関数. やはりbgrのチャンネル順なので画像の色が違います。
特定の色を抽出する
画像内の特定のカラー ブロックについては、必要なときに画像を反復処理して、そのカラー ブロックのみを含む画像を生成できます。たとえば、上のドラえもんの色分けは非常に明白で、この試みに非常に適しています。 。さらに、このアイデアの実現は、opencv の inRange 関数に依存します。
dst = cv2.inRange(src, lowerb, upperb)
上記の関数は、画像内の領域 [ lowerb, upperb ] の色を抽出することですが、グレースケール画像の場合、 lowerb は単なる整数値ですが、RGB 色空間の画像の場合は注意が必要です、 lowerb には色を記述するための行列が必要で、upperb は上記と同じです。そうですね、でも 3 チャンネルで色を表現するというハードウェア固有の機能は本当に頭の痛い問題です。HSV では色を表現する方法が 1 つしかないので、ユーザーにとっては非常に魅力的です。よし、これを試してみてください。
>>> import cv2
>>> import numpy as np
>>>
>>> mong = cv2.imread("among.png")
>>> mong_hsv = cv2.cvtColor(mong, cv2.COLOR_BGR2HSV)
>>> bmin, bmax = np.array((100, 43, 46)), np.array((125, 255, 255))
>>> mask = cv2.inRange(mong_hsv, bmin, bmax)
>>> blue = cv2.bitwise_and(mong, mong, mask=mask)
>>> ymin, ymax = np.array((26, 43, 46)), np.array((34, 255, 255))
>>> ymask = cv2.inRange(mong_hsv, ymin, ymax)
>>> rmin, rmax = np.array((0, 43, 46)), np.array((10, 255, 255))
>>> rmask = cv2.inRange(mong_hsv, rmin, rmax)
>>> yellow = cv2.bitwise_and(mong, mong, mask=ymask)
>>> red = cv2.bitwise_and(mong, mong, mask=rmask)
>>> cv2.imshow("source", mong)
>>> cv2.imshow("blue", blue)
>>> cv2.imshow("yellow", yellow)
>>> cv2.imshow("red", red)
>>> cv2.waitKey()
-1
上記は検索したHSV比較表をもとに実験したものですが、通常の色比較では写真の色が完全に一致していないはずで、得られるものも色のブロックの破片ばかりで非常に疲れます。
ただし、透かしの抽出により、この部分で予想外の利点が得られました。
>>> import cv2
>>> import numpy as np
>>>
>>>
>>> watermark = img[850:, 580:]
>>> wm_abstract = cv2.inRange(watermark, (230, 230, 230), (255, 255, 255))
>>> cv2.imshow("source", img)
>>> cv2.imshow("watermark_part", watermark)
>>> cv2.imshow("watermark", wm_abstract)
>>> cv2.waitKey()
-1
>>>
このように、ウォーターマークの一部を抽出しても、この種の純色のウォーターマークの場合は、RGBのinRangeの方が使いやすいです。
肌色のマーキングと検出
上記で決定した色範囲に従って、人物の写真に対応する肌色の色範囲をマークすると、対応する領域を取得して抽出できます。本では HSV を使用していますが、YCrCb はこのシーンにはもっと適しています。両方試してみてはいかがでしょうか。
本では肌色の色相と彩度を[5, 170]~[25, 166]に設定しているので、本の作例なのか分かりませんが、まずはこれを使用します。実験用に他の写真も使います。
>>> import cv2
>>> import numpy as np
>>>
>>>
>>> img = cv2.imread("eason.png")
>>> hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
>>> h, s, v = cv2.split(hsv)
>>> hmask = cv2.inRange(h, 5, 170)
>>> smask = cv2.inRange(s, 25, 166)
>>> mask = hmask & smask
>>> roi = cv2.bitwise_and(img, img, mask=mask)
>>> cv2.imshow("source", img)
>>> cv2.imshow("skin", roi)
>>> cv2.waitKey()
-1
結果を見る限り、肌色の検出はまだ可能ですので、次にYCrCbの検出を試してみます。上記の効果から、実際にはエッジ部分がうまく処理されていないため、以下ではopencvの4つのぼかし技術の1つであるハイパスフィルターを使用します。
# 引入部分和上面的一致
>>> ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
>>> y, cr, cb = cv2.split(ycrcb)
>>> cr = cv2.GaussianBlur(cr, (5, 5), 0)
>>> _, skin = cv2.threshold(cr, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
>>> cv2.imshow("res", skin)
>>> cv2.imshow("source", img)
>>> cv2.waitKey()
-1
効果はかなり良いのですが、作例を模写することしかできず、カラーの写真は得られません。
アルファチャンネル
RGB 色空間をベースに、透明か不透明かを示す A チャンネルを追加したものが RGBA 色空間です。A チャンネルの値の範囲は [0, 1] または [0, 255] です。一般的な画像処理では一般的に RGB チャネルが 3 つあるため、RGBA 色空間を使用するには、変換に cvtColor が必要です。これは単なる記録であり、拡張ではありません。
4. 画像操作
ああ、画像のビットプレーン分解、画像のXOR暗号化、電子透かし、顔の符号化・復号化などに画像加算やビット演算が使われているそうです。逆転するかどうかは今のところ不明だ。この部分の処理をPythonの対話型プログラミングで仮にデモしてみます。
添加
大きく分けて 2 種類あり、1 つは単純な + 演算子による加算であり、もう 1 つは opencv が提供する add 加算関数です。ルールを説明します。
- プラス演算子、画像 a と画像 b が単純なプラス演算子を使用してプラス演算を実行する場合、その結果が最大グレースケール値 255 を超えると、値はモジュロ 256 となり、剰余を超えない場合の加算値はこれになります。通常通り運用されます。
a + b { a + b , a + b ≤ 255 mod ( a + b , 256 ) , a + b > 255 a+b \begin{cases} a+b,&a+b\le255\\ mod(a+ b , 256),&a+b>255 \end{件}ある+b{ ある+b 、mod ( a _ _+b 、256 )、ある+b≤255ある+b>255- cv2.add(a, b)、画像 a と画像 b は、この関数を使用して加算されます。これは、上記のプラス演算子による加算と同じであり、結果も 2 つあります。が 255 より大きい場合、最大値として予約されます。255 は飽和値であり、上限が設定されています。255 を超えない場合は、通常どおり追加されます。
cv 2. add ( a , b ) { a + b , a + b ≤ 255 255 , a + b > 255 cv2.add(a, b) \begin{cases} a+b, &a+b\le255\\ 255, &a+b>255 \end{件}c v 2. a dd ( a ,b ){ ある+b 、255、ある+b≤255ある+b>255
簡単な例を見てみましょう
import cv2
import numpy as np
# 灰度图模式读取
img = cv2.imread('among.png', 1)
# 加法处理
a = img + img
b = a + a
c = cv2.add(img, img)
c = cv2.add(c, c)
cv2.imshow("a", a)
cv2.imshow("b", b)
cv2.imshow("c", c)
cv2.imshow("d", d)
cv2.waitKey()
演算結果は以下の通り、
aとbは加算演算子を用いた画像加算演算、cとdはcv2のadd関数を用いた加算演算であり、前者は明らかに線感があることが分かります。さらなる加算演算 色付きのものはどんどん暗くなり、後者はその逆で、より彩度が高くなって、ますます白く見えることがより明白です。これは単純な画像の処理ですが、2 つの画像の間、または値と画像の間の場合でも、参照がないため明確ではありません。
加重和
加重合計とは、2 つの画像のピクセル値の合計を計算するときに、画像の重みの要素を考慮することです。2 つの加重合計イメージは同じサイズとタイプである必要がありますが、チャネルと特定のタイプに対する要件はありません。ただし、私が見つけたブログ情報は基本的にスクリプト化されており、人間の参照に適した記述はありません (mdzz)。とりあえず、この本の概念をここにコピーして、次の式を使用しましょう:
dst = saturate ( src 1 × α + src 2 × β + γ ) dst = saturate(src1\times\alpha + src2\times\beta+ \ガンマ)d s t=飽和状態(ソース1 _ _ _ _ _ _×ある+src2 _×b+γ )
この重み付けされた合計を実現するには、opencv の関数 addWeighted を使用します。上記の式に対応して、5 つのパラメーターも渡す必要があります: src1、alpha、src2、beta、gama。いわゆる重みは、最終的な画像の結果ではどちらの比率がより明らかになるので、上記は次のように理解できます: 結果画像 = 画像 1 x 係数 1 + 画像 2 x 係数 2 + 明るさの調整量。
簡単な画像ブレンドの例:
import cv2
import numpy as np
a = cv2.imread('blena.png')
b = cv2.imread('bboat.png')
c = cv2.addWeighted(a, 0.2, b, 0,8, 0)
d = cv2.addWeighted(a, 0.5, b, 0,5, 0)
e = cv2.addWeighted(a, 0.8, b, 0,2, 0)
cv2.imshow("c", c)
cv2.imshow("d", d)
cv2.imshow("e", e)
cv2.waitKey()
リソースがまったくないので、Baidu ブックに掲載されている 2 枚の写真のスクリーンショットを撮って実験用に保存し、顔画像の割合の増加に応じて表示する必要がありました。結果は次のとおりです。明らかです
。比率がどんどん大きくなり、顔がはっきりと見えてきます。これは画像を混合した例です。画像のサイズが異なる場合は、リサイズを使用して調整できると聞きました。再試行する。画像を変更した後、コードは次のように調整されます。
import cv2
import numpy as np
a = cv2.imread("ayanamirei.jpg")
b = cv2.imread("asikaj.jpg")
# 比例调整大小,本来size并不一样,但调整后以外发现一致了,应该是算法问题
a = cv2.resize(a, None, fx=0.1, fy=0.1, interpolation=cv2.INTER_LINEAR)
b = cv2.resize(b, None, fx=0.1, fy=0.1, interpolation=cv2.INTER_LINEAR)
c = cv2.addWeighted(a, 0.2, b, 0.8, 0)
d = cv2.addWeighted(a, 0.5, b, 0.5, 0)
e = cv2.addWeighted(a, 0.8, b, 0.2, 0)
cv2.imshow("c", c)
cv2.imshow("d", d)
cv2.imshow("e", e)
cv2.waitKey()
調整はうまくいき、再びブレンド画像処理を行うことができました。得られたカラーマップは以下の通りです
相対的には0.4と0.3の比率が良く、背景が目立たない場合は下をクリアした方が良いと思います。サイズ変更の最後のパラメータの選択は、描画に大きな影響を与えます。
- INTER_NEAREST: 最近傍補間
- INTER_LINEAR: 線形補間 (デフォルト)
- INTER_AREA: 領域補間
- INTER_CUBIC: 3次スプライン補間
- INTER_LANCZOS4: ズームアウトには Lanczos 補間
cv2.INTER_AREA が推奨され、ズームインには cv2.INTER_CUBIC および cv2.INTER_LINEAR が推奨されます。
ビットごとの論理演算
論理演算には、AND or NOT、XOR などの演算があります。ここでのビット論理演算とは、数値を 2 進数に変換し、対応するビット番号ごとに論理演算を行うことです。特定の論理操作を拡張する必要はなく、記録されません。opencv が提供するビット単位の論理演算関数は次のとおりです。
- ビットごとの論理積、dst = cv2.bitwise_and(src1, src2[,マスク])
- ビットごとの論理和、dst = cv2.bitwise_or(src1, src2[,マスク])
- ビット単位ではありません。dst = cv2.bitwise_not(src[,マスク])
- ビットごとの XOR、dst = cv2.bitwise_xor(src1, src2[,マスク])
マスク、オプションの演算マスク、8 ビットの単一チャネル配列値
ビット単位の AND 演算の使用
ビット単位の AND 演算の場合、グレースケール画像を処理するときに、ピクセルと値 0 をビット単位で AND 演算して 0 のみが得られ、255 とのビット単位の AND が元の値になります。0 の値が多数ある画像と、 255 の値 ビットごとの AND の代わりに、切り取られたかのように、部分的に「黒くなった」画像が得られます。率直に言うと、別の画像を使用して、ターゲット画像を部分的または完全にカバーします。
import cv2
import numpy as np
img = cv2.imread("blena.png")
# 制作一个和原图同尺寸的数组
mask = np.zeros(img.shape, dtype=np.uint8)
# 固定区域设置纯白
mask[100:280, 100:250] = 255
a = cv2.bitwise_and(img, mask)
# 三图进行显示
cv2.imshow("source", img)
cv2.imshow("mask", mask)
cv2.imshow("res", a)
cv2.waitKey()
まあ、ビットごとの AND 演算の応用を除いて、残りの 2 つまたは 3 つは当面応用がないようです。ただし、ビット単位の論理演算の 2 つの行列は同じサイズである必要があり、そうでない場合はエラーが発生することに注意してください。したがって、画像のサイズを調整する用途はたくさんあります。
ビットプレーンの分解
カラー画像は、RGB 3 チャネルに従って 3 つの行列に分割できます (チャネルの分解)。画像の同じビット上のピクセルを結合して得られる画像はビット プレーンとも呼ばれ、このプロセスはビットプレーン分解。グレースケール イメージでは、ピクセルの値の範囲は 0 ~ 255 で、これは 1 バイト内の 8 ビットの範囲です。各ビットの値が抽出されてビット プレーンが取得され、元のイメージと合計すると、 9枚の画像のうち カラー画像をビットプレーンに分割すると多すぎます。したがって、ここでの例は単なるグレースケール画像であり、その方が簡単です。
グレースケール画像の場合、2進数に展開してビットプレーンを切り出すと、ビットプレーンが存在するビットの重みが大きいほど、ビットプレーンと元の画像との相関が高くなります。ビットプレーンが存在するビットの重み。対応する値が低いほど、元の画像との相関が低くなります。端的に言えば、2の0乗に相当する数字で切り出されたビットプレーンは元の画像からは見えにくく、2の7乗に相当する数字で切り出されたビットプレーンは元の画像に似ています。元の画像。
RGB カラー画像の場合、3 つのチャネルに分割され、3 つのチャネルに対応する色も 8 ビットの 2 進数であり、3 つのチャネルに分割し、対応する桁を同期してチャネルベースのビットプレーンを切り出します。そしてそれらを結合すると、元の画像のビット プレーンが得られます。まあ、カラーマップのビットプレーン分割はそれほど難しくないようです。
ビットプレーンのセグメンテーション手順:
- 元の画像の幅と高さを抽出し、同じサイズの行列を構築します。
- 上記の行列を、値が2 n 2^nのピクセルに構築します。2nの行列が抽出に使用されます。
- 抽出された行列と元の画像に対してビットごとの AND 演算を実行して、ビット プレーンを取得します。
- 小さい桁に対応するビット プレーンが純粋な黒として表示されないようにするには、最終結果が 0 や 255 などの白と黒の値のみになるか、true または false のいずれかになるように、しきい値を設定する必要があります。 。
import cv2
import numpy as np
img = cv2.imread("alian.jpg", 0)
img = cv2.resize(img, None, fx=0.4, fy=0.4, interpolation=cv2.INTER_LINEAR)
cv2.imshow("source", img)
w, h = img.shape
# 创建8层同规模的矩阵,每个矩阵用来放置对应数位的提取矩阵,在后面的循环中给对应矩阵赋值
arrays = np.zeros((w, h, 8), dtype=np.uint8)
for i in range(8):
x[:, :, i] = 2**i
# 循环对原图进行按位与运算提取位平面,然后进行阈值处理,最后输出图像
for i in range(8):
temp = cv2.bitwise_and(img, x[:, :, i])
# 将temp中大于0的值转为True,除此以外的值转换为False
mask = temp>0
# 将temp中True换成255
temp[mask] = 255
cv2.imshow("res"+str(i+1), temp)
cv2.waitKey()
上記から、グレースケール画像 (エイリアンのアバターが使用されていますが、残念ながら白すぎ、体が大きすぎるため、サイズを変更する必要があります) のビット プレーン抽出と表示が行われていることが明確にわかります。カラー画像を処理したい場合は、上記のプロセスでカラー画像を追加して 3 つのチャネルを分割し、最後に 3 つのチャネルを合成します。次のように:
import cv2
import numpy as np
img = cv2.imread("alian.jpg")
img = cv2.resize(img, None, fx=0.4, fy=0.4, interpolation=cv2.INTER_LINEAR)
cv2.imshow("source", img)
w, h = img.shape[:2]
b, g, r = cv2.split(img)
b_arr = np.zeros((w, h, 8), dtype=np.uint8)
g_arr = np.zeros((w, h, 8), dtype=np.uint8)
r_arr = np.zeros((w, h, 8), dtype=np.uint8)
for i in range(8):
b_arr[:, :, i], g_arr[:, :, i], r_arr[:, :, i] = 2**i, 2**i, 2**i
for i in range(8):
t1 = cv2.bitwise_and(b, b_arr[:, :, i])
t2 = cv2.bitwise_and(g, g_arr[:, :, i])
t3 = cv2.bitwise_and(r, r_arr[:, :, i])
mask1 = t1 >0
mask2 = t2 >0
mask3 = t3 >0
t1[mask1], t2[mask2], t3[mask3] = 255, 255, 255
temp = cv2.merge([t1, t2, t3])
cv2.imshow("res"+str(i+1), temp)
cv2.waitKey()
透かし
主要な Web サイトはすべて、自身の著作権問題を非常に懸念しており、Web サイトにさまざまなインプリントを入れることを待ちきれません。画像の透かしはその現れです。アップロードする場合もあり、それが Web サイトに表示されると、他人の痕跡であることは非常に嫌なことです。しかし、私人にとっては、これも保護すべき知的財産権の一部であり、世論の問題でもあります。
ビット プレーンは同じバイナリの桁に基づく画像のピクセルの集合であると上で紹介しました。桁が大きいほど、対応するビット プレーン画像は元の画像に近づき、桁が小さいほど、より正確になります。元のイメージとの違い。したがって、2 進数の最下位ビットは 2 の 0 番目の位置であり、最下位ビット (LSB、最下位ビット)とも呼ばれます。情報がこのビットに格納され、元の画像に結合されると、この情報は隠蔽されます。情報、ウォーターマークは、この種の隠蔽情報に属します。どうやって?
1 つ目は、透かしを入れる必要がある画像と透明な透かしのある画像を読み取り、後者のビット プレーンに対応する最下位ビットを抽出し、次に、画像に応じて適切な調整、またはズームインまたはズームアウトの調整を行うことです。画像のサイズを指定して、透かしを入れる必要がある画像に貼り付けます。この手順では、cv2 の加重合計関数または PIL の貼り付け関数を使用できます。
自作透かしマップ
まず、ウォーターマークの元の画像がないため、独自のラベルを使用して 2 つのウォーターマークのオリジナル画像を生成したいと考えています。1 つは黒の背景に白のテキスト、もう 2 つは白の背景に黒のテキストです。白黒の背景画像を生成するのは非常に簡単です。ピクセル値 0 は真っ黒、ピクセル値 255 は真っ白です。テキストの追加については、cv2 に付属の putText 関数を使用できます。さあ、構築を始めましょう。
import cv2
import numpy as np
# 制造黑色和白色背景
black_w = np.zeros((300, 450), dtype=np.uint8)
white_b = np.ones((300, 450), dtype=np.uint8)*255
# 调用putText函数添加手写体的标签,距离左上角150的位置,字体大小为3,粗细为3
cv2.putText(black_w, 'JackSAMA', (0, 150), cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, 3, 255, 2)
cv2.putText(white_b, 'JackSAMA', (0, 150), cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, 3, 0, 2)
cv2.imshow("black", black_w)
cv2.imshow("white", white_b)
cv2.waitKey()
透かし画像の準備ができました。実際、実際の状況に応じて生成できます。透かし機能がカスタマイズされている限り、画像のサイズに応じて調整することもできます。もちろん、同期して調整することもできます実像があれば。putText の関数パラメータは主に次のとおりです。
img = cv2.putText(img, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]])
img, 操作图像对象
text,添加的文本,一般都是英文,中文使用会乱码,暂时也还没解决
fontFace,用过标签语言的应该都知道这是字体类型的意思
fontScale,字体大小
color,对于灰度图,简单的0-255表示即可,如果是rgb彩图就要适用(b, g, r)进行表示
thickness,线条粗细,默认是1
lineType,线条类型,默认是8连接类型
bottomLeftOrigin,默认为False,这样文本就是横着来;输入为True就是文本竖着来
フォントフェイス | 分析する |
---|---|
cv2.FONT_HERSHEY_SIMPLEX | 通常のサンセリフ フォントは、一般的に使用される英語フォントです |
cv2.FONT_HERSHEY_PLAIN | 小さなサンセリフフォント |
cv2.FONT_HERSHEY_DUPLEX | 通常サイズのサンセリフ体 |
cv2.FONT_HERSHEY_COMPLEX | 通常のセリフフォント |
cv2.FONT_HERSHEY_TRIPLEX | 通常サイズのセリフフォント |
cv2.FONT_HERSHEY_COMPLEX_SMALL | セリフ簡略版 |
cv2.FONT_HERSHEY_SCRIPT_SIMPLEX | 手書き風フォント |
cv2.FONT_HERSHEY_SCRIPT_COMPLEX | 手書きフォント複合版 |
cv2.FONT_ITALIC | 斜体マーク |
線種 | 分析する |
---|---|
cv2.FILLED | 塗りつぶしタイプ |
cv2.LINE_4 | 4接続タイプ |
cv2.LINE_8 | 8接続タイプ |
cv2.LINE_AA | アンチエイリアシングによる滑らかなライン |
上記はopencvのフォントパラメータと線種の解釈ですが、自作ライブラリで置き換えることができると聞いたのですが、やり方がわかりません。しかし、アンチエイリアシングラインパラメータを追加した方がはるかに快適に見えるようです。
ウォーターマークを埋め込む
ドラえもんの絵にウォーターマークを追加します。
>>> import cv2
>>> import numpy as np
>>>
>>> mong = cv2.imread("among.png")
>>> mong.shape
(347, 272, 3)
>>> watermark = cv2.imread("white_black_sign.png")
>>> watermark.shape
(300, 450, 3)
# 调整水印原图大小和对应待处理图片补充空白
>>> watermark = cv2.resize(watermark, None, fx=0.3, fy=0.3, interpolation=cv2.INTER_AREA)
>>> cv2.imshow("", watermark)
>>> cv2.waitKey()
-1
>>> watermark.shape
(90, 135, 3)
>>> temp = np.ones(mong.shape, dtype=np.uint8)*255
>>> temp[210:300, 137:272] = watermark
>>> cv2.imshow("", temp)
>>> cv2.waitKey()
-1
# 进行加权和拼接,实现图片添加水印
>>> res1 = cv2.addWeighted(mong, 0.9, temp, 0.1, 0)
>>> cv2.imshow("", res1)
>>> cv2.waitKey()
-1
透かしを削除する
ウォーターマークを削除する多くの方法は、アーティストに上記で生成したような単色のウォーターマーク画像を作成してもらい、色の範囲を特定してから 2 つの画像をマージする、つまり貼り付けるというもので、さまざまな用途があります。 . PIL ライブラリの使用
import cv2
import PIL import Image
import numpy as np
img = cv2.imread("./iamfine.png")
h, w, _ = img.shape[0:3]
#切割,根据实际水印位置而定,[y0:y1, x0:x1]为下面裁剪用法,裁剪完后可以用上面的方法输出查看一下
cropped = img[int(h*0.9):h, int(w*0.75):w]
# 对图片进行阈值化处理,把由后面两个参数划定的RGB色彩空间范围外的色彩输出为0或者255,由图片底色确定这个范围
thresh = cv2.inRange(cropped, np.array([230, 230, 230]), np.array([250, 250, 250]))
#创建结构和尺寸的数据元素
kernel = np.ones((3, 3), np.uint8)
# 扩展待修复区域
watermask = cv2.dilate(thresh, kernel, iterations=10)
specular = cv2.inpaint(cropped, watermask, 5, flags=cv2.INPAINT_TELEA)
#保存去除水印的残图
cv2.imwrite("new.png", specular)
# 用PIL的paste函数把残图粘贴在原图上得到新图
i = Image.open("./img/iamfine.png")
i2 = Image.open("./img/new.png")
i2.paste(i, (int(w*0.75), int(h*0.9), w, h))
i2.save("final.png")
2 つの写真の比較:
|
実は、上記のウォーターマークを追加する方法を使用して、反転することができます。
キャラクターの絵を生成する
画像はピクセルで構成されており、コンピューターは画像をバイナリで保存します。ピクセルの保存に使用されるビットは画像の深度であり、1 ビットで保存されます。画像は黒か白のいずれかです。なぜなら、0 しかないからです。および 1 つのオプション。1 バイト (8 ビット) を使用して 0 ~ 255 の値を格納します。色には赤、緑、青の三原色があり、この三色を重ねて他の色を表現することができ、この絵の画素の色は一般に3チャンネルと呼ばれるRGBの3色によって決まります。それぞれ 3 つの文字を使用しているため、各セクションはそれぞれ 3 原色の個々の値を表しており、これらを積み上げるとピクセルの色となり、通常は (0-255, 0-255, 0-255) ) ピクセル表現で。
私たちが生成したい文字画像は、実際にはピクセルから文字へのマッピングを確立することです。文字セットも非常に大きく、最も基本的な ASCII セットでも 128 文字であるためですが、それほど多くの文字を使用する必要はありません。いくつかの単純な文字を使用して文字セットを作成し、それをピクセルのカラー テーブルと組み合わせます (マッピング ルールがカスタマイズされているため)。
真実はこれですが、他の人の例は理解できませんでしたが、別の方法で多くのことを得ることができました。つまり、最初に画像をグレースケール画像、つまり白黒画像に変換します。この時点では、ピクセルを大量の文字に変換するのが簡単です。
簡単な操作は次のとおりです。
import cv2
import numpy as np
str = "#+-."
img = cv2.imread("among.png", 0)
# 此时就只有height和width两个值,没有depth
h, w = img.shape[0:2]
for_change = np.ndarray([h, w])
font = cv2.FONT_HERSHEY_SIMPLEX
for i in range(0, h, 5):
for j in range(0, w, 5):
t = str[round(3-img[i, j]/255*3)]
cv2.putText(for_change, t, (j, i), font, 0.1, color=(255, 255, 255))
cv2.imshow("", for_change)
cv2.waitKey(0)
cv2.imwrite("asciiPic.png", for_change)
まあ、少なくともそれは実現していますが、これには問題があります。つまり、ベースマップまで一緒に置き換えられると、少し目を引くことになります。
キャラクタービデオを生成する
ビデオはフレーム単位のピクチャであるため、キャラクタ ピクチャの生成はキャラクタ ビデオの生成の半分であり、上記のロジックを繰り返すことでキャラクタ ビデオを生成できます。
import cv2
import numpy as np
def pixel2char(pixel):
char_list = "@#$%&erytuioplkszxcv=+---. "
index = int(pixel / 256 * len(char_list))
return char_list[index]
def get_char_img(img, scale=4, font_size=5):
# 调整图片大小
h, w = img.shape
re_im = cv2.resize(img, (w//scale, h//scale))
# 创建一张图片用来填充字符
char_img = np.ones((h//scale*font_size, w//scale*font_size), dtype=np.uint8)*255
font = cv2.FONT_HERSHEY_SIMPLEX
# 遍历图片像素
for y in range(0, re_im.shape[0]):
for x in range(0, re_im.shape[1]):
char_pixel = pixel2char(re_im[y][x])
cv2.putText(char_img, char_pixel, (x*font_size, y*font_size), font, 0.5, (0, 0, 0))
return char_img
def generate(input_video, output_video):
# 1、读取视频
cap = cv2.VideoCapture(input_video)
# 2、获取视频帧率
fps = cap.get(cv2.CAP_PROP_FPS)
# 读取第一帧,获取转换成字符后的图片的尺寸
ret, frame = cap.read()
char_img = get_char_img(cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY), 4)
# 创建一个VideoWriter,用于保存视频
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
writer = cv2.VideoWriter(output_video, fourcc, fps, (char_img.shape[1], char_img.shape[0]))
while ret:
# 读取视频的当前帧,如果没有则跳出循环
ret, frame = cap.read()
if not ret:
break
# 将当前帧转换成字符图
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
char_img = get_char_img(gray, 4)
# 转换成BGR模式,便于写入视频
char_img = cv2.cvtColor(char_img, cv2.COLOR_GRAY2BGR)
writer.write(char_img)
writer.release()
if __name__ == '__main__':
generate('in.mp4', 'out.mp4')
さて、このビルド。。。。。。我慢できない、世代が終了する前にコンピューターが煙を出し始める!!!ルー先生がタバコを吸っています!!!切断後のビデオは、元のビデオの長さの半分ではありませんが、サイズは元のビデオの数倍になります。!!後で最適化しましょう。搾乳できない場合は搾乳できません。
上で使用した写真
綾波レイの作品もありましたが、大きすぎて見送らせていただきました。まずこれを実行して、後で記録しましょう。ビデオもあります: さて、このリソースを入手するのはとても難しいようです。
最近個人サイトを立ち上げ、色々な記事をまとめていきたいと思いますので、興味のある方はぜひご覧ください。