OpenGLをC言語で扱う①
空ウィンドウを表示する
まずはとりあえずウィンドウだけを表示してみる
これ自体はすごく簡単なので先にサンプルコードを示す
#include<stdio.h> #include<GL/glut.h> void display(void); int main(int argc,char *argv[]){ //call window glutInit(&argc,argv); glutCreateWindow("sample window");//string literal or argv(string) glutDisplayFunc(display); glutMainLoop(); return 0; } void display(){ //pass }
まずここで登場する関数について説明
関数名 | 機能 |
---|---|
void glutInit(int *argcp, char **argv) | OpenGLの初期化を行いmain関数のパラメータをそのまま渡す |
int glutCreateWindow(char *name) | ウィンドウを開きタイトルバーの設定などを行う |
void glutDisplayFunc(void (*func)(void)) | ウィンドウ内に描画する関数。描画内容は関数のポインタとして渡した関数に依存 |
void glutMainLoop(void) | イベントを待ち受ける関数 |
ざっくりまとめるなら
初期化→ウィンドウ作成→描画内容固めた関数渡して→ループさせる
みたいな感じで空ウィンドウは作成出来る
ただ一つ描画内容を固めるdisplayという独自関数がいつ呼ばれるのか
メインループがglutCreateWindowでウィンドウの生成が完了した段階で呼び出される
さらに言うなら再描画が必要なときも呼び出される
ウィンドウを塗りつぶしてみる
空ウィンドウのプログラムを実行するとわかるがウィンドウ下がそのまま表示されてしまっている
しかも位置は最初に呼び出した場所で固定
これではあまりに微妙な感じが出ているのでとりあえずウィンドウの背景を塗りつぶしてみる
#include<stdio.h> #include<GL/glut.h> void display(void); void init(void); int main(int argc,char *argv[]){ //OpenGLの初期化 glutInit(&argc,argv); //ディスプレイの表示モードの指定 glutInitDisplayMode(GLUT_RGBA); glutCreateWindow("sample window ver 2"); glutDisplayFunc(display); //描画で使うものの初期化 init(); glutMainLoop(); return 0; } void display(){ glClear(GL_COLOR_BUFFER_BIT); glFlush(); } void init(){ glClearColor(0.0,0.0,0.0,1.0); }
ここで初めて出てきた関数の名前と機能
関数名 | 機能 |
---|---|
void glutInitDisplayMode(unsigned int mode) | ディスプレイの表示モードを指定する関数、GLUT_RGBAとGLUT_INDEXが使える |
void glClearColor(GLclampf R, GLclampf G, GLclampf B, GLclampf A) | ウィンドウを塗りつぶす色を指定する関数、α値は1で不透明となる |
void glClear(GLbitfield mask) | ウィンドウを塗りつぶす関数、塗りつぶすバッファを指定して使う |
glFlush(void) | まだ実行されてない命令をすべて実行させる関数。あまり呼び出しすぎると描画速度が低下する |
ここでは色の指定などそういった初期化をinit(void)にまとめている
main関数やdisplay関数にまとめてもいいんけど、今後描画するものが多くなった時、複雑になった時などに
そういう記述が邪魔になるので分割する
2次元図形の描画と色の変更
今回はとりあえず今回は正方形を描画してみるが、ここで注意しなければならない点は座標は[-1,1]の範囲で固定されているという点で
これはx軸y軸の両方に言えることで、簡単に言えば単位円がちょうど書ける大きさというわけである
#include<stdio.h> #include<GL/glut.h> void display(void); void init(void); int main(int argc,char *argv[]){ glutInit(&argc,argv); glutInitDisplayMode(GLUT_RGBA); glutCreateWindow("square 2d"); glutDisplayFunc(display); init(); glutMainLoop(); return 0; } void display(void){ glClear(GL_COLOR_BUFFER_BIT); //描画 //描画タイプの指定 glBegin(GL_LINE_LOOP); //線の色を指定 glColor3d(0.0,0.0,1.0); //座標を設定 glVertex2d(0.8,0.8); glVertex2d(-0.8,0.8); glVertex2d(-0.8,-0.8); glVertex2d(0.8,-0.8); //設定終了 glEnd(); //図形だけじゃ見づらいのでx,y軸も追加 glBegin(GL_LINES); glColor3d(0.0,0.0,0.0); glVertex2d(-1,0); glVertex2d(1,0); glEnd(); glBegin(GL_LINES); glColor3d(0.0,0.0,0.0); glVertex2d(0,-1); glVertex2d(0,1); glEnd(); glFlush(); } void init(void){ //黒が背景は見づらいので白に変更 glClearColor(1.0,1.0,1.0,1.0); }
今回は表にするまでもなく手順が簡単なのでざっくり説明
glBeginで描画タイプ設定
↓
glColor3dで線の色を設定できるので必要に応じて設定
↓
glVertex2dで座標を設定x,yの順番
↓
glEndで設定終了、描画
glFlushで強制的に描画できる
また描画タイプを変更することで図形内部を塗りつぶしたりもできる
RaspberryPiにSambaをインストールしてサーバー化する
はじめに
結構前にRaspberryPI B+を買っていて
そのままになっていたのを発見したのでsambaでもインストールしてサーバー化しようと思う
sambaの設定
インストールすると/etc/samba/smb.confというsambaの設定ファイルが生成される
なのでこれを
sudo vim /etc/samba/smb.conf
とかやって開く
そうすると見ればわかるがsambaの設定は[]で区切られている
今回は全体の設定をいじるので[global]の設定を変更する
Samba の導入 - Linux で自宅サーバ [ Home Server Technical. ]
設定についてはこのサイトが詳しくのっているので丸投げ
sambaにユーザーを追加する
このままでは使用できないのでsambaにユーザーを追加する
今回はraspbianで操作しているのでまずraspbian側にユーザを追加する
もう存在するユーザを使うのであればここは無視していい
sudo adduser [USERNAME]
ここからraspbianに存在するユーザを追加する作業
sudo pdbedit -a [USERNAME]
後はそれをsambaに
sudo smbpasswd -a [USERNAME]
これで使えるようになる(はず)
追記(2016/02/28)
raspbianにインストールするとprintersとprint$っていう設定グループがデフォルトであるけど
これがあるとなかなか設定が変更しにくいので[printers]がある行から下を全部コメントアウトして
以下をその下に記述するとなかなか扱い易かった
[Share] path = [公開したいフォルダのパス] writable = yes guest ok = no guest only = no create mode = 0777 directory mode = 0777 share modes = yes
完全に利用をユーザ登録されている人に限定した設定になっているので
ゲストもokにしたいのなら
guest ok = yes
guest onlu = yes
に変更してください
移動平均法
移動平均法
今回は移動平均法を自分で実装してみた
そもそも移動平均法とは、一枚の画像に含まれるノイズを除去する基本的な手法の一つで
入力画像における(i,j)成分の濃度に対しその近傍の濃度値の平均値を出力画像の(i,j)成分とする方法のこと
その過程により画像の濃度のばらつきを低下させるため必然的にノイズも除去されるが
出力画像の輪郭などが鈍る傾向にある
具体的な処理
近傍の濃度値の平均は以下の式で求めることが出来る
ここでの[]はガウス記号
実際この通りにコードを書いていけばいいが
プログラミングで扱う以上リストや配列の範囲を超える場合があるので
その時のループはスキップすることで実装できる
コード
#coding:utf-8 import numpy as np import math import cv2 #ガウス記号が示すものを関数化したもの def gauss(number): if 0 <= number: return int(math.floor(number)) else: return int(math.ceil(number)) image = "lenna256.png" result = "lenna256_move.png" #今回は256次正方行列を使うため #256*256画素にリサイズしたlenna256.pngを使用 img = cv2.imread(image) #グレースケールに変換 gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY) #移動平均法を行う for row in range(0,len(gray)): for line in range(0,len(gray[0])): #近傍濃度の和 s = 0 #3x3の正方形で計算する n = 3 for i in range(-1*gauss(n/2.0),gauss(n/2)+1): for j in range(-1*gauss(n/2.0),gauss(n/2)+1): if len(gray) <= i + row or len(gray[0]) <= j + line: continue s += gray[i+row][j+line] gray[row][line] = s/n**2 #cv2.imwrite(result,gray) cv2.imshow("move average",gray) cv2.waitKey(0) cv2.destroyAllWindow()
判別分析法
判別分析法
判別分析法とはざっくり言えば2値化の手法の一つで
分離度という値が最大になる値を見つけ、自動的に2値化する手法
分離度はクラス間分散とクラス内分散の比で求めることができる
具体的な仕組み
今、しきい値を
として
これがまず判別分析法を使う上でのデータの定義
そして次がクラス内分散とクラス間分散、全分散の定義
ここまでが判別分析法で扱う定義の部分
これら与えられた条件を元に分離度を算出する
そしてこれが最大になるしきい値tを求めることで2値化する
しかし全分散はしきい値に関係なく一定なのでクラス間分散の分子
が最大になるしきい値tを求めることになる
なので0~255の間で上の式が最大になるようなtを求めていけばいい
そしてそれらの条件を満たすtを見つけたら
それを元に2値化処理をすることで、判別分析法が実装できる
実装コード
#coding:utf-8 import numpy as np import cv2 img = cv2.imread("lenna256.png") #画像をグレースケールに変換 gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY) #ヒストグラムを作成 histgram = [0]*256 for i in range(0,len(gray)): for j in range(0,len(gray[0])): histgram[gray[i][j]] += 1 #print histgram max_t = max_val = 0 #判別分析法を使って2値化 for t in range(0,256): #画素数 w1 = w2 = 0 #クラス別合計値 sum1 = sum2 = 0 #クラス別平均 m1 = m2 = 0.0 for i in range(0,t): w1 += histgram[i] sum1 += i*histgram[i] for j in range(t,256): w2 += histgram[j] sum2 += j*histgram[j] #0除算を防ぐ if w1 == 0 or w2 == 0: continue #クラス別平均の算出 m1 = sum1/w1 m2 = sum2/w2 #結果を算出 result = w1*w2*(m1-m2)*(m1-m2) if max_val < result: max_val = result max_t = t for i in range(0,len(gray)): for j in range(0,len(gray[0])): if(gray[i][j] < max_t): gray[i][j] = 0 else: gray[i][j] = 255 #print max_val #cv2.imwrite("binary_lenna.png",gray) cv2.imshow("binary",gray) cv2.waitKey(0) cv2.destroyAllWindow()
せっかくなので今回はヒストグラムも自作してみた
OpenCV+Pythonで顔にモザイク処理をする
顔にモザイク
最近はずっと画像処理の理論的な部分ばかり勉強していて、これといった成果物を出してないから
簡単に作れるものをとりあえず作ってみようっていうことで
今回は、OpenCV+Pythonで顔にモザイクをかけてみた
顔認識とかはよくわからないしそういったことはまだ勉強してないけど
とりあえず色々調べながら作ってみたけど、なにせpythonは基礎文法レベルでしか書けないから
今回はいろいろと苦労するところがあった
ただリスト形式で画像をあれこれいじれたのでなかなか楽しかった
仕組み
まず顔認識をしようとしても、どういう仕組みかわからない
というわけで画像処理速報さんで調べた結果、やりたいことに近いものがあり
そこで仕組みを説明していたので、自分なりにまとめてみる
実際にこれから紹介する順番でコードを書いている
今回はカスケード型分類器なるものを使った
1,対象画像を読み込む(変数は2つ用意しそれぞれ同じ画像)
2,顔認識用のカスケード型分類器を取得
3,カスケード型分類器で顔認識を行う
4,検出した情報に基づいて顔部分を切り取る
5,切り取った顔の部分にモザイク処理をほどこす
6,モザイク部分を結果出力用画像の一部に貼り付ける
7,結果を表示する
といった具合で顔認識以外はOpenCVで行えるかなり初歩的な処理を組み合わせることで実現できる
(その初歩的な処理を実装できなくて苦戦した)
コード
#coding:utf-8 import numpy as np import cv2 #顔探索用のカスケード型分類器を取得 face_cascade = cv2.CascadeClassifier(haarcascade_frontalface_default.xmlのパスを渡す) img = cv2.imread("Lenna.png") result = cv2.imread("Lenna.png") #読み込んだ画像をグレースケールに変換 gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY) #分類器で顔を認識する face = face_cascade.detectMultiScale(gray,1.3,5) if 0 < len(face): print "get face" for (x,y,w,h) in face: #顔の部分だけ切り抜いてモザイク処理をする cut_img = img[y:y+h,x:x+w] cut_face = cut_img.shape[:2][::-1] #10分の1にする cut_img = cv2.resize(cut_img,(cut_face[0]/10, cut_face[0]/10)) #画像を元のサイズに拡大 cut_img = cv2.resize(cut_img,cut_face,interpolation = cv2.cv.CV_INTER_NN) #モザイク処理した部分を重ねる result[y:y+h,x:x+w] = cut_img else: print "no face" cv2.imshow("face mosaic",result) #cv2.imwrite("output file name",result) cv2.waitKey(0) cv2.destroyAllWindows()
こんな具合で割と短いコードになった
注意すべきは7行目のカスケード型分類器の取得を行う部分で、ここで渡すのはカスケード分類器のファイル名を含めたパスであること
他のサイトではファイル名のみを渡していたりするがそれだと顔認識が正常に行われない上にエラーとしてもでてこない
つまりバグになるので注意
python入門
はじめに
最近OpenCVをpythonで扱うことが多くなったので一度しっかりと勉強してみる
今回は自分の備忘録として書くようなスタイルなので、ある程度プログラミング言語を他に扱えることを想定している
なので、ところどころ説明を省くので全くの初心者が見るべきではない
それと多々、javaと比較することがある(自分が一番得意としているから)
今回はpython2.7.6を使用していく
文法以前の基本的な事項
ここではまず基本的な事項、コメントやソースエンコードなどそういったことをまとめていく
変数
まずは数値や文字列などをやる前に変数について
pythonで変数は
hello = "hello,world" print hello #print->hello,world
のように
変数名 = オブジェクト
という形をとり、作成とオブジェクトの代入がまとめて行われる
つまりこれまでのjavaやCといった言語のように宣言だけ先にしておくことはできない
それと関係して一度もオブジェクトが代入されていない変数を使用しようとすると「NameError」が発生する
また型推論なので変数にデータ型を指定することは必要ない
変数名の付け方はいつもどおり
オブジェクトの再代入はデータ型の制約がないため、全く別のオブジェクトでも代入可能つまり
number = 123 number = 3.14 #浮動小数点もok number = "math"#極論これもok
更に変数を変数に代入することもいつもどおり可能
その他のプログラミング言語で採用してるもろもろのこともできる
ただし数値と文字列は演算することはできなく
num = 1 str = "hello" print str+num
ということもできない
あと二項演算子も使用できる
数値
数値リテラルはいつもどおりなところが多いけど型推論な分多少注意が必要なのが
長整数と複素数
12000000000000000000000000000L#長整数 0+2j#複素数
長整数はざっくり言えばlongのことだから末尾にL,lが必要
だけど実際はこれすら推論されるから変数に代入するときは大して気にする必要がない
問題は複素数
実部虚部ともに宣言可能で虚部にはjをつける
また以下のように、実部虚部それぞれ取り出すことができる
c = 1 + 2j print c.real print c.imag
四則演算は少し今までの言語と違う部分がある
基本的に+,-,*,%,/が使えることに変わりはないが、**という累乗を表す演算子と//という小数点以下切り捨ての演算子があり
以下のように使用する
print 17.0//5 #print->3.0 print 5 ** 2 #print->25
累乗は
基数 ** 指数
という形になる
さっきの演算のところでもいったが文字列と数値は演算できない
そのため文字列を整数に変換することを考える
更には小数も整数に変換することを考える
まず前提として以下のようなことをすると
1 + "2"
「TypeError」が発生する
そこでint関数というものを使用しこうする
print 1 + int("2") #print -> 3
int関数は結構便利で基数を設定することもできる
print int("10",10) #基数10 print -> 10 print int("10",8) #基数8 print -> 8 print int("10",16) #基数16 print -> 16
なので基数を16にした場合はA~Fまでのアルファベットも使えるが
10の時にそれらが含まれていると「ValueError」がでる
小数に対して使用された場合は0の方向に小数部が丸められる
文字列
リテラルはいつもどおりのダブルクォーテーション
ただしpythonはトリプルクォーテーションなるものがある
"""hello,world"""
これは複数行にわたって文字列が宣言でき、"""内の改行は\nに置き換えられる
更に便利なのはraw文字列というものがあって先頭にr,Rをつけると文字列内の\はそのまま扱われる
数値と文字では演算ができなかったが文字列同士では+で連結することができる
print "hello"+"world" #print -> helloworld
pythonには更に謎な機能があって
文字列を繰り返すことができる
"hello"などの文字列の後に * 回数 とすることでその文字列を繰り返すことができる
print "hello" * 3 #print -> hellohellohello
文字列の長さがlen()関数で行う
print len("hello") #print -> 5
数値と文字列は連結できないので文字列を整数にする方法を紹介した
今回はその逆で数値を文字列に変換する
それはstr関数によって行われ整数以外に小数も扱う
print 1 + str(2) #print -> 12
文字列オブジェクトはインデックスを指定することで文字を切り出すことができる
hello = "hello" print hello[0] #print -> h
さらに文字だけでなく部分文字列も切り出すことができて
以下のようにする
hello = "hello" print hello[1:3] #print -> el
条件分岐
いつもどおりのif文から
ただしいつもなら{}を使うがpythonはブロックがインデントで分けられるので
ちょっと勝手が違う
更にいうとelseifはelifでelseはいつもどおりelse
age = 18 if age < 20: print "未成年" elif 20 <= age: print "成人" else: print "エラー"
ここで注意したいのがtrue or false
pythonではTrue or Falseで先頭が大文字になる
また0は偽として扱われ、それ以外は真になる
空文字列も同様に偽となり空出ないなら真である
空のタプル、リスト、ディレクトリは偽であり真はない
Noneも同様
比較演算子はいつもどおりのものが揃っていてそれ以外はない
ちなみに==で文字列も評価できるがそれは値を比較しているだけであってオブジェクトを比較しているわけではない
なのでオブジェクトを比較するときはis,is notを使用する
また論理演算子は少し違って
&& -> and
-> or |
! -> not
となる
リスト
pythonでは配列とjavaのCollectionのlistを足して2で割ったようなリストというものがある
宣言では{}ではなく[]を使用して以下のようにする
list = [1,2,3,4,5,6]
また含まれるオブジェクトは1種類に統一する必要はなく、数値と文字列のように混ざっててもいい
また更にいうとリストオブジェクトは別のリストオブジェクトを要素にできるから局所的に多次元配列のようにもできる
要素をインデックスで参照するときは
#list = [1,2,3,4,5,6]を仮定 print list[0] #print -> 1
となる、つまりいつもどおり
文字列でスライスを使って部分文字列を取得した時のように
リストに対してもそれは同様におこなえる
ただ説明してなかったのは、スライスで片方を省略したり、負の数が使えることである
#上記のlistを仮定 print list[3:] #print -> 4,5,6 print list[:3] #print -> 1,2,3 print list[1:-1] #print -> 2,3,4,5
要素の変更はいつもどおり行えるが
要素数の違う部分配列をまとめて変更することも出来る
リストのサイズに関してはlenで取得できる
リストに要素を追加する場合はappend関数が使える
別のリストの要素を追加するときはextend関数を使う
list = [1] list.append(2) list.append(3) list.extend([4,5,6]) print list #print -> 1,2,3,4,5,6
またリスト同士の連結は+で連結出来る
さらに文字列と同じようにリストを繰り返すことも出来る
listにインデックスを指定して要素を追加したい場合は
insert関数を使う
list = [1,2,3] list.insert(1,4)#insert(インデックス,要素) print list #print->1,4,2,3
リストの要素を削除するときはdelが使える
list = [1,2,3] del list[0] print list#print->2,3
指定インデックスの要素を削除するときはpop関数でインデックスを渡す
逆に指定の要素を持つ部分を削除するときはremove関数で引数に要素を渡す
スライスを使えばこれらのことが意外と簡単にできたりする
リスト[len(リスト):] = オブジェクト
で末尾に挿入ができる
また部分リストに対して[]を代入することでその要素を消すことができるが
スライスで部分リストを取得してない時には使えない
リストにある要素が存在するかどうかを確かめたいときはinを使う
list = [1,2,3,4,5] if 2 in list: print "yes" #print -> yes
逆に存在しない時はnot in
指定した要素が存在しそのインデックスを返すのはindex関数
これに引数としてインデックスを確かめたい要素を渡す
存在しないなら「ValueError」がでる
リストに含まれるある要素の個数を数えたいならcount関数に
カウントしたい要素を渡す
ソートはsortとreverseでjavaのCollectionsと同じ
連続した数値のリストを作成したいときはrange関数を使う
list = range(5) print list #print -> 0,1,2,3,4 list2 = range(2,5) print list2 #print -> 2,3,4 開始位置を宣言することができる list3 = range(1,6,2) print list3 #print 1 -> 1,3,5 stepを宣言できる
タプル
リストとほぼ同じことができるが宣言がすこし違う
tuple = (1,2,3,4,5)
と()でくくる
ただし要素がひとつだけの時は
tuple = (1,)
と最後に,をつける必要がある
要素の取得はリスト同様[]でインデックスを指定する
スライスとサイズの取得も同様、連結と繰り返しに関しても同じことが言える
tuple関数というものを使えばリストからタプルを生成できたりもする
辞書オブジェクト
これはjavaでいうMapとほぼおなじ
dict = {"java":1,"python",2}
のように{}で宣言する
キーに対応する値を取得したいなら[]でインデックスのようにキーを渡す
値の変更とサイズの取得もリストやタプルに同様
連結は
辞書オブジェクト.update(辞書オブジェクト)
キーの削除はdelが同じように使える
辞書オブジェクトに対してはpop関数は削除したキーに対応する値を返す
任意のキーを削除するならpopitem関数を使用
このとき選ばられるセットはランダムである
clearですべて削除できる
キーが含まれているかを確かめるのはin
キー in 辞書オブジェクト
not inも使える
inと同じ動作をするものでhas_key()もある
またkeys関数を実行することでキーをすべてリストとして取得できる
それは値にも使えて値をリストで取得したいときはvalues関数
(キー、値)のタプル形式ですべて取得するときはitems関数
ループ
まずはwhile文
while True: print "always true"
となる、つまりいつもどおり
ただしループを抜けた時の処理をelseで書ける
while True: print "always true" else: print "error"
次にfor文
for i in [1,2,3,4,5,6]: print i #print i -> 1,2,3,4,5,6
for文に対してもelseは適応できる
更に今はリストを渡していたがrange関数でわたすこともできる
画像処理におけるヒストグラム
はじめに
今回は画像処理をするとき(2値化などで)に使うヒストグラムというものについてまとめる
ヒストグラムとは
ヒストグラムとは画像処理以外の分野で使われることもあるが
画像処理の分野では、各濃度値に対してその濃度値を持った画素数を求めたもので、濃度ヒストグラム
または単純にヒストグラムという。
ヒストグラムは主に横軸が濃度値、縦軸に画素数をとったグラフで表現される
ヒストグラムはその画像がどのような濃度値を持った画素から成り立っているかの情報をまとめたもので
画像処理において処理が容易なのもあり非常に有用な手段といえる
ヒストグラムの性質
ヒストグラムにはいくつかの性質がある
まずはヒストグラムは各画素の濃度値だけをとりあげているので、その画素の場所には関係しないグラフとなる
なのでその濃度値を持った画素がどれくらいあるかはわかってもその画素がどこにあるかまではわからない
つまり画像の持つ空間的特徴は考慮されずその情報はヒストグラム上では失われる
次に、ヒストグラムは同一の画像に対しては一意に、つまり一通りに定まるが
異なる画像であってもそれと同一のヒストグラムを持つことも可能性としてないわけではない
最後にヒストグラムは画像の各画素の濃度値を集めたのもなので
画像を分割してヒストグラムを求めた場合、それらの和が全体のヒストグラムと一致する
ヒストグラムの計算法
大きさMxN画素の濃度レベルがNGLの画像imgのヒストグラムを求め
配列hstに格納する方法でC言語ライクな擬似コードでしめす
また配列hstの初期値は各要素とも0であるとする
int i,j; int img[N][M]; int hst[NGL]; for(i = 0; i < N; i++){ for(j = 0; j < M; j++){ hst[img[i][j]]++; } }
濃度レベルが256で8bitだとすると
ヒストグラムは0〜255までの範囲となる
実用例
ヒストグラムの具体的な使い方は後述することにして
今回はpythonとOpenCVを使ってヒストグラムを表示するものを紹介する
例によりこのLenna.pngの画像を使う
結果
#coding:utf-8 import cv2 import pylab as plt img = cv2.imread("lenna256.png") hist = cv2.calcHist([img],[0],None,[256],[0,256]) plt.plot(hist) plt.xlim([0,256]) plt.show()
今回はカラーのままでやったがグレースケールにするとまた少し違って見える
ヒストグラム計算を自作してみる
ヒストグラムを求めること自体は結構簡単なので画像の入出力だけにOpenCVを使い
それ以外を自作するなんていうことも可能
#coding:utf-8 import numpy as np import cv2 img = cv2.imread("lenna256.png") #画像をグレースケールに変換 gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY) #ヒストグラムを作成 histgram = [0]*256 for i in range(0,len(gray)): for j in range(0,len(gray[0])): histgram[gray[i][j]] += 1