PythonでOpenCVさわってみた

OpenCV インストール

PythonでOpenCVを使う@Mac - Qiita

ここ参照.

基本コード

# coding: utf-8

import cv2
import math
import numpy as np

# ファイル名の指定
file_src = '2.png'
file_dst = '2.png'

# 画像の読み込み
img_src = cv2.imread(file_src, 1)

# ウィンドウ名の指定
cv2.namedWindow('src')
cv2.namedWindow('dst')

# 画像処理
# 上下反転
img_dst = cv2.flip(img_src, flipCode=0)

# 出力
cv2.imshow('src', img_src)
cv2.imshow('dst', img_dst)

# 終了処理
cv2.waitKey(0)
cv2.destroyAllWindows()

以下のコードは基本コードの 画像処理の下に書いていくもの

様々な色空間への変換

# 複数色チャンネルの分割と結合 BGR->RGB
img_bgr = cv2.split(img_src)
img_dst = cv2.merge((img_bgr[0],img_bgr[1],img_bgr[2]))

# BGRからHSVへの変換
img_dst = cv2.cvtColor(img_src, cv2.COLOR_BGR2HSV)

# BGRからグレースケールへの変換
img_dst = cv2.cvtColor(img_src, cv2.COLOR_BGR2GRAY)

ガンマ変換

# ガンマ変換 ガンマ値は2.0
# ルックアップテーブルの生成
Y = np.ones((256, 1),dtype= 'uint8') * 0
for i in range(256):
    Y[i][0] = 255 * pow(float(i)/ 255, 1.0 / 2.0)
# ルックアップテーブル変換
img_dst = cv2.LUT(img_src, Y)

アフィン変換

# アフィン変換
# 原点中心45°反時計回り
# その後に,y座標方向に画像高さの半分だけ平行移動
size = tuple(np.array([img_src.shape[1], img_src.shape[0]]))
afn_mat = np.float32(
    [
        [math.cos(-math.pi / 4), -math.sin(-math.pi / 4), 0],
        [math.sin(-math.pi / 4), math.cos(-math.pi / 4), img_src.shape[0] * 0.5]
    ])
img_dst = cv2.warpAffine(img_src, afn_mat, size, flags=cv2.INTER_LINEAR)

center = tuple(np.array([img_src.shape[1] * 0.5, img_src.shape[0] * 0.5]))
angle = 45.0
scale = 1.0
size = tuple(np.array([img_src.shape[1], img_src.shape[0]]))
rot_mat = cv2.getRotationMatrix2D(center,angle,scale)
img_dst = cv2.warpAffine(img_src, rot_mat, size, flags=cv2.INTER_LINEAR)

透視変換

# 透視変換
size = tuple(np.array([img_src.shape[1], img_src.shape[0]]))
pts1 = np.float32([
                    [160,479],
                    [480,479],
                    [480,240],
                    [160,240]
                ])
pts2 = np.float32([
                    [160,479],
                    [480,479],
                    [400,240],
                    [240,240]
                ])
psp_mat = cv2.getPerspectiveTransform(pts1, pts2)
img_dst = cv2.warpPerspective(img_src, psp_mat, size)

濃淡ヒストグラム

# 濃淡ヒストグラム
img_dst = np.zeros([100, 256]).astype('uint8')
rows, cols = img_dst.shape

# 度数分布を求める
hdims = [256]
hranges = [0, 256]
hist = cv2.calcHist([img_src],[0], None, hdims, hranges)

# 度数の最大値を取得
min_val , max_val, min_loc, max_loc = cv2.minMaxLoc(hist)
for i in range(0, 255):
    v = hist[i]
    cv2.line(img_dst, (i, rows), (i, rows - rows * (v / max_val)), (255,255,255))

# ネガポジ変換
img_dst = 255 - img_ssrc

明度調整 

# 明度調整
shift = 100
table = np.arange(256, dtype=np.uint8)
for i in range(0,255):
    j = i + shift
    if j < 0:
        table[i] = 0
    elif j > 255:
        table[i] = 255
    else:
        table[i] = j
img_dst = cv2.LUT(img_src, table)

# コントラスト低減
min = 0
max = 255
img_dst = cv2.normalize(img_src, alpha=min, beta=max, norm_type=cv2.NORM_MINMAX)

フィルタ処理

# 平均化オペレータ
img_dst = cv2.blur(img_src, (3,3))

# バイラテラルオペレータ
img_dst = cv2.bilateralFilter(img_src, 11, 1, 1)

# 中央値フィルタ処理
img_dst = cv2.medianBlur(img_src ,9)

# Sovelオペレータ
img_tmp = cv2.Sobel(img_src, cv2.CV_32F, 1, 0)
img_dst = cv2.convertScaleAbs(img_tmp)

# 2次微分オペレータ
img_tmp = cv2.Laplacian(img_src, cv2.CV_32F, 3)
img_dst = cv2.convertScaleAbs(img_tmp)

# 鮮鋭化フィルタ
k = 1.0
op = np.array([
                [-k, -k, -k],
                [-k, 1 + 8 * k, -k],
                [-k, -k, -k]
            ])
img_tmp = cv2.filter2D(img_src, -1, op)
img_dst = cv2.convertScaleAbs(img_tmp)

参考文献

OpenCVによる画像処理入門 (KS情報科学専門書)

OpenCVによる画像処理入門 (KS情報科学専門書)

Pythonでスタックとキュー

スタック

class Stack:
    def __init__(self, stack = None,max = 100):
        self.max = max
        if type(stack) is type([]):
            self.stack = stack
        elif stack is None:
            self.stack = []
        else:
            print('Please list type!\n')

    def push(self, e):
        if not self.is_full():
            self.stack.append(e)    
            return self.stack
        else:
            print('Stack is full!!\n')      

    def pop(self):
        if not self.is_empty():
            e = self.stack.pop()
            return (e, self.stack)
        else:
            print('Stack is empty!!\n')  


    def is_empty(self):
        if len(self.stack) is 0:
            return True
        else:
            return False

    def is_full(self):
        if len(self.stack) is self.max:
            return True
        else:
            return False        
        

キュー

 class Queue:
    def __init__(self, queue = None, max = 100):
        self.max = max
        if type(queue) is type([]):
            self.queue = queue
        elif queue is None:
            self.queue = []
        else:
            print('Please enter list type!\n')

    def enqueue(self, e):
        if not self.is_full():
            self.queue.append(e)   
            return self.queue
        else:
            print('Queue is full!!\n') 

    def dequeue(self):
        if not self.is_empty():
            e = self.queue[0]     
            del self.queue[0]     
            return (e, self.queue)  
        else:
            print('Queue is empty!!\n')

    def is_empty(self):
        if len(self.queue) is 0:
            return True
        else:
            return False

    def is_full(self):
        if len(self.queue) is self.max:
            return True
        else:
            return False        

Python3 文法メモ

クロージャ

def closure(param):
    def inner():
        return "Hello %s" % param
    return inner

c1 = closure("MARIO")
print(c1()) # Hello MARIO
c2 = closure("LUIGI")
print(c2()) # Hello LUIGI

ラムダ

def hello(names, func):
    for name in names:
        func(name)

names = ["MARIO", "LUIGI", "DONKY"]

hello(names, lambda name: print(name+"!"))

ジェネレータ

def my_range(first=0, last=10, step=1):
    number = first
    while number < last:
        yield number
        number += step

ranger = my_range(1,5)
for i in ranger:
    print(i)

デコレータ

def document_it(func):
    def new_function(*args, **kwargs):
        print('Running function:', func.__name__)
        print('Positional arguments:',args)
        print('Keyword arguments:',kwargs)
        result = func(*args, **kwargs)
        print('Result:',result)
        return result
    return new_function

@document_it
def add_ints(a,b):
    return a+b

print(add_ints(3,5))

プロパティ

class Duck():
    def __init__(self,input_name):
        self.__name = input_name

    @property
    def name(self):
        print("inside the getter")
        return self.__name

    @name.setter
    def name(self, input_name):
        print("inside the setter")
        self.__name = input_name

f = Duck("Howard")
print(f.name)
f.name = "One"
print(f.__name) # ここでエラーを吐く

参考文献

入門 Python 3

入門 Python 3

Cython 文法メモ

# ライブラリのインポート
from libc.math cimport sin
from libc.string cimport strcmp
from libc.stdio cimport fopen,fclose,fgets,fseek,ftell,fwrite

# 関数宣言
def main():

    #変数宣言
    cdef int a=1
    cdef char* s = "Hello マリオさん。こんにちは"
    cdef list l = [1,2,3,4,5]
    cdef dict d = {"one":1,"two":2}
    cdef tuple t = (1,2)
    cdef set se = {"one","two","three"}
    
    # fopenでのファイル読み込み
    fin = fopen("add.pyx","r")
    if not fin:
        print "読み込みミスった"
    cdef char str[1000] 
    fseek(fin, 0L, 1); # ファイルシーク
    while fgets(str,256,fin) != NULL:
        # print ftell(fin)
        print str
    fclose(fin) # ファイル閉じる

    # ファイル書き込み
    fout = fopen("output.txt","w")
    if not fout:
        print "読み込みミスった"
    fwrite(s, sizeof(char), len(s), fout);
    fclose(fout)

参考URL

Cython ドキュメント(和訳) — Cython 0.17.1 documentation

CythonとPythonとの処理時間の比較

Pythonを高速化するCythonを使ってみた - Kesin's diary

ここに書いてあることがおもしろそうだったのでやってみた.

1~100000000まで足しこむプログラム. 環境はmacで。

C

#include <stdio.h>
int main(){
   int i,n=0;
   for(i=0;i<100000000;i++)
       n+=i;
}

オプションで最適化しとく.

gcc -O2 a.c
time ./a.out      
./a.out  0.00s user 0.00s system 31% cpu 0.007 total

うん。速い。

Python

n=0
for i in range(100000000):
    n += i

同じく

time python3 add.py 
python add.py  13.15s user 1.53s system 97% cpu 15.021 total

遅い.

型指定していないCython

def add_test():
    n=0
    for i in range(100000000):
        n+=i

で、setup.pyを

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [Extension("add", ["add.pyx"])]

setup(
  name = 'Hello world app',
  cmdclass = {'build_ext': build_ext},
  ext_modules = ext_modules
)

として,main.pyを以下の通りに作成して

from add import add_test
add_test()

として実行すると

time python3 main.py
python3 main.py  5.75s user 0.02s system 99% cpu 5.830 total

だいぶ速くなった!

型指定したCython

def add_test():
    cdef int n = 0
    cdef int i
    for i in range(100000000):
        n+=i

あとはさっきと同様にして実行

time python3 main.py
python3 main.py  0.03s user 0.01s system 68% cpu 0.064 total

速すぎわろた。

グラフにすると

こんな感じ.

f:id:carumisu:20151218154250p:plain

httpdをソースコードからインストール

CentOSで行った.

インストール

yum install -y gcc make pcre pcre-devel wget

cd /usr/local/src
wget http://ftp.yz.yamagata-u.ac.jp/pub/network/apache//apr/apr-1.5.2.tar.gz
tar xzvf apr-1.5.2.tar.gz 
cd apr-1.5.2
./configure --prefix=/opt/apr/apr-1.5.2
make
make test
make install

cd /usr/local/src
wget http://ftp.jaist.ac.jp/pub/apache//apr/apr-util-1.5.4.tar.gz
tar -xvzf apr-util-1.5.4.tar.gz
cd apr-util-1.5.4
./configure --prefix=/opt/apr-util/apr-util-1.5.4 --with-apr=/opt/apr/apr-1.5.2
make
make test
make install 

cd /usr/local/src
wget http://ftp.jaist.ac.jp/pub/apache/httpd/httpd-2.4.18.tar.gz
tar -xvzf httpd-2.4.18.tar.gz
cd /usr/local/src/httpd-2.4.18
./configure --prefix=/opt/httpd/httpd-2.4.18 --with-apr=/opt/apr/apr-1.5.2 --with-apr-util=/opt/apr-util/apr-util-1.5.4
make
make install

動的モジュールのインストール

# httpd開発ツール
yum install -y httpd-devel

そして

# mod_infoを入れる場合
httpd -M | grep info # 何も出ない場合, 未インストール
cd /Apacheのソース/modules/generators/
apxs -i -a -c mod_info.c

httpd.conf側で

LoadModule info_module modules/mod_info.so

<IfModule mod_info.c>
  <Location /server-info>
    SetHandler server-info
    Order deny,allow
    Deny from all
    Allow from 127.0.0.1
  </Location>
</IfModule>

としてhttpdを再起動

Pythonの文法での疑問 set list

>>> a = [set([])]*7
>>> a
[set(), set(), set(), set(), set(), set(), set()]

こんな感じでsetのリストを作ってa[1]だけに1を入れようとしたところ

>>> a[1].add(1)
>>> a
[{1}, {1}, {1}, {1}, {1}, {1}, {1}]

ってなるのなんでだろう...って思って Twitterで呟いたら、ありがたいリプが!!!

話は簡単で

a = [set([])] * 7

とすると

「set([]) が 7個コピーされて代入される」ので a[0]もa[1]も同じオブジェクトだからだそうです。

正しいやり方は

a = [ [] for i in range(7) ]

よく考えたら当たり前な話でした。

リプくださった皆さん、本当にありがとうございました。

D3.js 折れ線グラフ メモ

だんだん慣れてきた.

[
{
"DATE": "2015/11/27 15:47:29", 
"TOTAL_RECORDS": 265
}, 
{
"DATE": "2015/11/27 15:50:47", 
"TOTAL_RECORDS": 461
}, 
{
"DATE": "2015/11/27 15:53:58", 
"TOTAL_RECORDS": 647
}, 
{
"DATE": "2015/11/27 15:58:14", 
"TOTAL_RECORDS": 829
}, 
{
"DATE": "2015/11/27 16:27:18", 
"TOTAL_RECORDS": 1008
}, 
{
"DATE": "2015/11/30 11:18:07", 
"TOTAL_RECORDS": 1198
}, 
{
"DATE": "2015/12/01 10:34:28", 
"TOTAL_RECORDS": 1364
}, 
{
"DATE": "2015/12/03 10:49:03", 
"TOTAL_RECORDS": 1977
}, 
{
"DATE": "2015/12/03 10:50:18", 
"TOTAL_RECORDS": 2156
}, 
{
"DATE": "2015/12/04 10:29:15", 
"TOTAL_RECORDS": 2347
}, 
{
"DATE": "2015/12/07 10:44:02", 
"TOTAL_RECORDS": 2509
}
]

というjsonファイルの可視化

d3.json(dirpath+"total_records.json", function(error, data){

    // 軸の表示のためのマージンと画面サイズ
    var margin = {top: 20, right: 20, bottom: 30, left: 40};
    var width = 1000 - margin.left - margin.right;
    var height = 300 - margin.top - margin.bottom;

    // svgタグの作成
    var svg = d3.select("#total_records")
                .append("svg")
                .attr({ //元の画面サイズ
                    width: width + margin.left + margin.right,
                    height: height + margin.top + margin.bottom
                })
                .append("g") // マージンの分だけ位置をずらす
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    // x軸の最小最大
    var xMin = new Date(data[0]["DATE"]);
    var xMax = new Date(data[data.length - 1]["DATE"]);
    // y軸の最小最大
    var yMin = Math.min.apply(null,data.map(function(o){return o["TOTAL_RECORDS"];}));
    var yMax = Math.max.apply(null,data.map(function(o){return o["TOTAL_RECORDS"];}));

    // スケールの定義
    var xScale = d3.scale.linear()
                            .domain([xMin,xMax])
                            .range([0,width]);
    var yScale = d3.scale.linear()
                            .domain([yMin-10, yMax])
                            .range([height, 0]);

    var fmtFunc = d3.time.format("%Y/%m/%d");

    // 軸の設定
    var xAxis = d3.svg.axis()
                        .scale(xScale)
                        .orient("bottom")
                        .tickSize(6, -height)
                        .tickFormat(function(d){ return fmtFunc(new Date(d))});

    var yAxis = d3.svg.axis()
                            .ticks(5) //軸のチックの数
                            .scale(yScale)
                            .orient("left")
                            .tickSize(6, -width);

    // line設定
    var line = d3.svg.line()
                        .x(function(d,i){ return xScale(new Date(d["DATE"]));})
                        .y(function(d,i){ return yScale(d["TOTAL_RECORDS"]);});

    // svgタグの中にcircleタグの作成
    svg.selectAll("circle")
        .data(data)
        .enter()
        .append("circle")
        .attr({
            r: 5,
            cx: function(d,i){ return xScale(new Date(d["DATE"])); },
            cy: function(d,i){ return yScale(d["TOTAL_RECORDS"]);}
        });

    // line表示
    svg.append("path")
        .datum(data) //data読み込み
        .attr({
            class: "line",
            d: line //上で作ったlineを入れて、linepathを作る
        });

    // gタグの中でyAxisをcallし、y軸を作る
    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append("text")
        .attr({
            x: 10,
            y: -10
        })
        .style("text-anchor", "end")
        .text("Number");

    svg.append("g")
            .attr({
                class: "x axis",
                transform: "translate(0," + height + ")"
            })
            .call(xAxis);

});

参考URL

D3.jsチュートリアル Archives - Data is fun.

D3.js 縦棒グラフ メモ

スケールでだいぶ苦戦した。

以下は

item
18
18
0
30
47
0
1

を読み込んで縦棒グラフの作成のメモ.

d3.csv(dirpath+"hoursA.csv", function(error, data){

        // 描画サイズ
    var width = 550;
    var height = 200;
    
        // svgタグの定義   
    var svg = d3.select("#hoursA").append("svg")
                    .attr("width", width)
                    .attr("height", height)

        // 棒の幅
    // var barWidth = 40;

        // データ読み込み
    var dataset = [];
    for(var i=0; i<data.length;i++){
        dataset.push(data[i].item);
    }

    // スケール関数を作成
    var xScale = d3.scale.linear()
                                .domain([0,dataset.length]) // [入力の最小値,入力の最大値]
                                .range([0,width]); // [出力の最小値, 出力の最大値]

        // ブラウザは下に伸びていくのでrangeに注意
    var yScale = d3.scale.linear()
                                .domain([0,Math.max.apply(null,dataset)]) // [入力の最小値,入力の最大値]
                                .range([height,30]); // [出力の最小値, 出力の最大値] 
 
    
    var rects = svg.selectAll("rect")
                    .data(dataset);

    // パディング
    var paddingY = 15

        // 棒の作成
    rects.enter()
            .append("rect")
            .attr({
                width: barWidth,
                height: function(d,i){ return height - yScale(d);},
                x: function(d,i){ return i * (width/dataset.length);},
                y: function(d,i){ return yScale(d) - paddingY;},
                fill: "rgba("+0+","+255+","+0+","+0.5+")"
            });

        // 棒の長さを数値化
    rects.enter()
            .append("text")
            .attr({
                class: "barNum",
                x: function(d,i){ return i * (width/dataset.length) + barWidth/2;},
                y: function(d,i){ return yScale(d)- paddingY - 5;}
            })
            .text(function(d,i){ return d;});    

        // 横軸の作成
    svg.append("rect")
        .attr({
            class: "axis_x",
            width: width,
            height: 1,
            x: 0,
            y: height - paddingY -1
        });

        // 横軸の数値
    rects.enter()
        .append("text")
        .attr({
            class: "barNume",
            x: function(d,i){ return i * (width/dataset.length);},
            y: height
        })
        .text(function(d,i){
            return i;
        });
});

f:id:carumisu:20151209002336p:plain

こんな感じになると思う

D3.js 基本文法

使う機会が発生したのでメモ。

D3.jsの組み込み

<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>

基本操作

# DOM要素を指定して変数に代入
# bodyの中に、属性wisth,heightを持ったsvgタグを生成

var dataset = [11,62,23,54,33,51,32,16];
var w = 1000;
var h = 300;
var svg = d3.select("body").append("svg").attr({width: w, height: h});

# DOM要素を指定して編集. 存在していないタグも指定可能 (後でappendとかする)
# 1つだけのときはselect
# 複数あるときはselectAll

svg.selectAll("rect")
            .data(dataset) // datasetを結びつける
            .enter()            // 今回の場合、rectタグすべて
            .append("rect") // タグの生成
            .attr({                 // 属性の設定
                x: padding,
                y: function(d,i){ return i * 25 ;},
                width :function(d) { return xScale(d) - padding;},
                height: 20,
                fill: "red"
            })
            .on("click", function(d){ alert(d);});  // イベントの設定

アニメーション

svg.selectAll("circle")
    .data(dataset)
    .enter()
    .append("circle")
    .transition()               // アニメーションの設定
    .delay(function(d, i){    // 要素間の時間間隔の設定
        return i * 300;
    })
    .duration(1000)  // 何秒かけてするか
    .ease("bounce") // 要素の指定位置への着地の仕方
    .each("start", function(){  // コンストラクタみたいなもの
        d3.select(this).attr({    // 今回の場合、このthisはcircleにあたる
            fill: "green",
            r: 0,
            cy: h
        });
    })
    .attr({
        cx: function(d, i){ return 50 + (i * 100); },
        cy: h/2,
        r: function(d){ return d;},
        fill: "red"
    })
    .each("end", function(){ // デストラクタみたいなもの
        d3.select(this)
            .transition()
            .duration(1000)
            .attr({
                fill: "blue",
                r: 0,
                cy : 0
            });
    });

コード全体

htmlファイル

<!DOCTYPE html>
<html>
<head>
   <meta charset="utf-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <title></title>
   <link rel="stylesheet" href="">
   <style>
        .axis path
        .axis line {
            fill: none;
            stroke: black;
        }
        .axis text {
            font-size: 11px;
        }
    </style>
</head>
<body>

    <button id="update">データ更新</button>

    <svg id="myGraph" width="1000" height="200">
        
    </svg>

    <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script src="./test.js" charset="utf-8"></script>

    
</body>
</html>

jsファイル

// 初期化
var padding = 20;
var svg = d3.select("#myGraph");
var update = d3.select("#update");
var w = svg.attr("width");
var h = svg.attr("height");


d3.csv("./test.csv", function(error, data){
    var dataset = [];
    for(var i=0; i<data.length;i++){
        dataset.push(data[i].item1);
    }
    console.log(dataset);
    var xScale = d3.scale.linear()
                    .domain([0,Math.max.apply(null,dataset)])
                    .range([padding,w - padding])
                    .nice();

    var xAxis = d3.svg.axis()
                    .scale(xScale)
                    .orient("bottom");

    svg.append("g")
        .attr({
                class: "axis",
                transform: "translate(0,150)"
            })
        .call(xAxis);

    svg.selectAll("rect")
        .data(dataset)
        .enter()
        .append("rect")
        .attr({
            x: padding,
            y: function(d,i){ return i * 25; },
            width: function(d){return xScale(d) + "px";},
            height: 20,
            fill: "red"
        })
        .on("mouseover", function(d){
                d3.select(this).attr("fill","orange");
            })
        .on("mouseout", function(d){
                d3.select(this).attr("fill","red");
            });
});

参考