可変ブログ

色々メモとか。とりあえず自分が分かるように。その後、なるべく人が見て分かるように。可変。

SOM (自己組織化マップ) 作ってみた。




ここら辺を参考に。



#ifdef _DEBUG
//Debugモードの場合
#pragma comment(lib,"C:\\opencv\\build\\x86\\vc12\\lib\\opencv_world300d.lib")            // opencv_core
#else
//Releaseモードの場合
#pragma comment(lib,"C:\\opencv\\build\\x86\\vc12\\lib\\opencv_world300.lib") 
#endif

#include <iostream>
#include <vector>
#include <stdlib.h>

#include "opencv2/opencv.hpp"
#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"

#include <omp.h>

using namespace std;
using namespace cv;

class Node{
public :
	vector<double> param;
	Node(){
		param = vector<double>(3);
	}
};

class SomLearn{
	int learning_range;
	double learning_rate;
public:
	vector< vector<Node> > map;
	SomLearn(int x, int y){
		map = vector< vector<Node> >(x, vector<Node>(y));
		InitMap();
	}
	int InitMap(){
		srand(10);
		for (int i = 0; i < map.size(); ++i){
			for (int j = 0; j < map[0].size(); ++j){
				for (int k = 0; k < map[0][0].param.size(); ++k){
					map[i][j].param[k] = double(rand() / double(RAND_MAX) * 255.0);
				}
			}
		}
		return 0;
	}
	double GetEuqridDinstance(Node data1, Node data2){
		double n = 0;
		for (int i = 0; i < data1.param.size(); ++i){
			n += (data2.param[i] - data1.param[i]) * (data2.param[i] - data1.param[i]);
		}
		return n;
	}
	int GetMinIndex(Node data, int &i_index, int &j_index){
		double min_val = 0;
		double temp = 0;
		i_index = 0;
		j_index = 0;

		min_val = GetEuqridDinstance(data, map[0][0]);
		for (int i = 0; i < map.size(); ++i){
			for (int j = 0; j < map[0].size(); ++j){
				temp = GetEuqridDinstance(data, map[i][j]);
				if (min_val > temp){
					min_val = temp;
					i_index = i;
					j_index = j;
				}
			}
		}
		return 0;
	}
	int Exposure(Node data){

		int i_index = 0;
		int j_index = 0;
		GetMinIndex(data, i_index, j_index);

		int i_small = i_index - learning_range;
		int i_big = i_index + 1 + learning_range;
		int j_small = j_index - learning_range;
		int j_big = j_index + 1 + learning_range;

		if (i_small < 0){ i_small = 0; }
		if (map.size() < i_big){ i_big = map.size(); }
		if (j_small < 0){ j_small = 0; }
		if (map[0].size()< j_big){ j_big = map[0].size(); }

		for (int i = i_small; i < i_big; ++i){
			for (int j = j_small; j < j_big; ++j){
				for (int k = 0; k < data.param.size(); ++k){
					map[i][j].param[k] += (data.param[k] - map[i][j].param[k]) * learning_rate;
				}
			}
		}
		return 0;
	}
	int Train(vector<Node> &node, int learning_range_in, double learning_rate_in){
		learning_range = learning_range_in;
		learning_rate = learning_rate_in;

		for (int i = 0; i < node.size(); ++i){
			Exposure(node[i]);
		}
		return 0;
	}
};

int main(){
	vector<Node> node(100);
	srand(1000);
	for (int i = 0; i < node.size(); ++i){
		for (int k = 0; k < node[0].param.size(); ++k){
			node[i].param[k] = double(rand()) / double(RAND_MAX) * 255.0;
		}
	}

	int x = 30;
	int y = 30;
	int lrange = 6;
	double lrate = 0.01;

	SomLearn som(x, y);
	Mat src(Size(som.map.size(), som.map[0].size()), CV_8UC3, Scalar::all(0));
	Mat dst(Size(som.map.size(), som.map[0].size()), CV_8UC3, Scalar::all(0));

	for (int i = 0; i < som.map.size(); ++i){
		for (int j = 0; j < som.map[0].size(); ++j){
			for (int k = 0; k < som.map[0][0].param.size(); ++k){
				src.at<Vec3b>(Point(i, j))[k] = som.map[i][j].param[k];
			}
		}
	}

	som.Train(node, lrange, lrate);
	for (int i = 0; i < som.map.size(); ++i){
		for (int j = 0; j < som.map[0].size(); ++j){
			for (int k = 0; k < som.map[0][0].param.size(); ++k){
				dst.at<Vec3b>(Point(i, j))[k] = som.map[i][j].param[k];
			}
		}
	}



	//resize(src, src, Size(), 25, 25);
	//resize(dst, dst, Size(), 25, 25);
	imshow("src", src);
	imshow("dst", dst);

	waitKey(0);

	return 0;
}




f:id:shibafu3:20200522102142p:plain

voronoi図を作ってみた。

ここのアルゴリズムを参考にして作りました。




voronoifield.hpp

#ifndef VORONOIFIELD_HPP
#define VORONOIFIELD_HPP

#include "opencv2/opencv.hpp"
#include <vector>
#include <omp.h>


class Voronoi{
	double CalcDistance(cv::Point a, cv::Point b){
		return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
	}
	void CellProcessing(cv::Point center, cv::Point side){
		if ((side.x < 0) || (image_size.x <= side.x) || (side.y < 0) || (image_size.y <= side.y)){ return; }

		if (cells[side.x][side.y].flag == MOTHER){
			if (cells[center.x][center.y].flag == NONE){
				cells[center.x][center.y].flag = DETECTED;
				cells[center.x][center.y].distance = CalcDistance(center, side);
				cells[center.x][center.y].point_mother = side;
			}
			else if (cells[center.x][center.y].flag == DETECTED){
				if (cells[center.x][center.y].distance > CalcDistance(center, side)){
					cells[center.x][center.y].distance = CalcDistance(center, side);
					cells[center.x][center.y].point_mother = side;
				}
			}
		}
		else if (cells[side.x][side.y].flag == DETECTED){
			if (cells[center.x][center.y].flag == NONE){
				cells[center.x][center.y].flag = DETECTED;
				cells[center.x][center.y].distance = CalcDistance(center, cells[side.x][side.y].point_mother);
				cells[center.x][center.y].point_mother = cells[side.x][side.y].point_mother;
			}
			else if (cells[center.x][center.y].flag == DETECTED){
				if (cells[center.x][center.y].distance > CalcDistance(center, cells[side.x][side.y].point_mother)){
					cells[center.x][center.y].distance = CalcDistance(center, cells[side.x][side.y].point_mother);
					cells[center.x][center.y].point_mother = cells[side.x][side.y].point_mother;
				}
			}
		}
	}
public:
	class cell_info{
	public:
		int flag;
		cv::Point point_mother;
		double distance;
		cell_info(){
			flag = 0;
			point_mother = cv::Point(-1, -1);
			distance = 0;
		}

	};
	cv::Point image_size;
	double max_distance;
	std::vector<std::vector<cell_info> > cells;

	int NONE;
	int MOTHER;
	int DETECTED;

	Voronoi(){
		NONE = 0;
		MOTHER = 1;
		DETECTED = -1;
	}
	Voronoi(cv::Point size){
		NONE = 0;
		MOTHER = 1;
		DETECTED = -1;

		SetMap(size);
	}
	void SetMap(cv::Point size){
		image_size = size;
		cells = std::vector<std::vector<cell_info> >(image_size.x, std::vector<cell_info>(image_size.y));
		max_distance = CalcDistance(cv::Point(0, 0), size);
	}
	void SetPoints(std::vector<cv::Point> points){
		for (std::vector<cv::Point>::iterator itr = points.begin(); itr < points.end(); ++itr){
			if ((itr->x < 0) || (image_size.x <= itr->x) || (itr->y < 0) || (image_size.y <= itr->y)){ return; }
			cells[itr->x][itr->y].flag = MOTHER;
		}
	}
	void CreateVoronoi(){
		for (int k = image_size.x; k >= 1; k /= 2){
			double naname = sqrt(k*k + k* k);
#pragma omp parallel for
			for (int i = 0; i < image_size.x; ++i){
				for (int j = 0; j < image_size.y; ++j){
					CellProcessing(cv::Point(i, j), cv::Point(i - k, j - k));
					CellProcessing(cv::Point(i, j), cv::Point(i + k, j - k));
					CellProcessing(cv::Point(i, j), cv::Point(i - k, j + k));
					CellProcessing(cv::Point(i, j), cv::Point(i + k, j + k));
					CellProcessing(cv::Point(i, j), cv::Point(i, j - k));
					CellProcessing(cv::Point(i, j), cv::Point(i, j + k));
					CellProcessing(cv::Point(i, j), cv::Point(i - k, j));
					CellProcessing(cv::Point(i, j), cv::Point(i + k, j));
				}
			}
		}
	}
	void GetImage(cv::Mat &image){
#pragma omp parallel for
		for (int i = 0; i < image_size.x; ++i){
			for (int j = 0; j < image_size.y; ++j){
				image.at<unsigned char>(cv::Point(i, j)) = cells[i][j].distance * (512 / max_distance);
			}
		}
	}
	void GetData(cv::Mat &image, double max_d){
#pragma omp parallel for
		for (int i = 0; i < image_size.x; ++i){
			for (int j = 0; j < image_size.y; ++j){
				image.at<double>(cv::Point(i, j)) = cells[i][j].distance / max_d;
			}
		}
	}
};

#endif




main.cpp

#include "voronoifield.hpp"

/*標準入出力ライブラリ*/
#include <iostream>
/*可変長リストライブラリ*/
#include <vector>
/*OpenCVライブラリ*/
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui_c.h"
/*処理時間測定用ライブラリ*/
#include <time.h>

#include <omp.h>

using namespace std;
using namespace cv;


int main() {
	int i = 2;
	Voronoi voronoi(Point(128 * i, 128 * i));

	//vector<Point> points(7);

	//points[0] = Point(32 * i, 32 * i);
	//points[1] = Point(20 * i, 10 * i);
	//points[2] = Point(4 * i, 50 * i);
	//points[3] = Point(50 * i, 33 * 8);
	//points[4] = Point(100 * i, 100 * i);
	//points[5] = Point(100 * i, 10 * i);
	//points[6] = Point(10 * i, 100 * i);


	vector<Point> points;
	for (int x = 100; x < 140; ++x){
		for (int y = 100; y < 130; ++y){
			points.push_back(Point(x, y));
		}
	}

	for (int x = 10; x < 14; ++x){
		for (int y = 10; y < 13; ++y){
			points.push_back(Point(x, y));
		}
	}

	voronoi.SetPoints(points);
	voronoi.CreateVoronoi();

	Mat image = Mat(voronoi.image_size, CV_8UC1, Scalar::all(0));
	voronoi.GetImage(image);
	imshow("voronoi", image);
	waitKey(0);

	return 0;

}

Arduinoで外部割込みとシリアル割り込み

・外部割込み

簡単なスケッチを

#define INT_PIN 2

void gpio_isr(){
  Serial.println("warikomi");
}


void setup() {
    Serial.begin(115200);
    
    pinMode(INT_PIN, INPUT); // 割り込みのためのピンを設定
    attachInterrupt(digitalPinToInterrupt(INT_PIN), gpio_isr, FALLING);  // 割り込み開始
}

void loop(){
}

pinMode()で割り込みに使うピンを入力に設定
attachInterrupt() で割り込みに使うピンとコールバック関数とピンの状態がどういうときに割り込みを発生させるかを設定。
ピンは普通のピン番号と割り込みに使うピン番号は違うらしいのでdigitalPinToInterrupt() で変換



・シリアル割り込み
void serialEvent() { } という名前でやりたい処理の関数を定義します。
ただ、割り込みと言っても loop()関数が一周したあとにserialEvent() が実行されているだけなので、割り込みっぽくないです。



ArduinoとEthernetシールドを使ってTCP通信

ArduinoEthernetシールドの互換品を使ってTCP通信をします。
今はEthernetシールド2というものがあるらしいですが、今回使用するのは1の方だと思います。(たぶん)
Ethernetシールド2の場合、使うライブラリやArduinoIDEのバージョンが変わってくるので注意してください。
ちなみに純正品はもう1も2も生産していないらしいです。

使ったシールドはこれ




ここを参考に。



簡単なスケッチを。

#include <SPI.h>
#include <Ethernet.h>

// Enter a MAC address and IP address for your controller below. The IP address will be dependent on your local network:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 177);

// Initialize the Ethernet server library with the IP address and port you want to use (port 80 is default for HTTP):
EthernetServer server(19227);
EthernetClient client;

char eth_recv_buf[128];
char eth_send_buf[37] = "M SD8 124 01 23 45 67 89 AB CD EF \r\n";
char eth_recv_len;
char eth_send_len;


void EthWrite(){
    if (Serial.available()){
        one_char = Serial.read();
        if (one_char == 'M'){
            serial_itr = 0;
        }

        serial_recv_buf[serial_itr] = one_char;

        if ((one_char == '\n') && (serial_itr == 35)){
            client.write(serial_recv_buf, 36);
        }
        if ((++serial_itr) > 36) {
            serial_itr = 0;
        }
    }
}


void setup() {
    // Open serial communications and wait for port to open:
    Serial.begin(115200);
    while (!Serial) { ; }

    Ethernet.init(10);  // Most Arduino shields
    Ethernet.begin(mac, ip);

    if (Ethernet.hardwareStatus() == EthernetNoHardware) {
        while (true) {
            delay(1); // do nothing, no point running without Ethernet hardware
        }
    }

    // start the server
    server.begin(); // listen相当

    pinMode(8, OUTPUT);
}

char c;
void loop() {
    // listen for incoming clients
    client = server.available(); // accept相当
    if (client) {
        while (client.connected()) {
            if (eth_recv_len = client.available()) {
                digitalWrite(8, HIGH);
                c = client.read(eth_recv_buf, eth_recv_len);
                Serial.write(eth_recv_buf, eth_recv_len);
            }
            
            EthWrite();
            digitalWrite(8, LOW);
        }

        delay(100);
        client.stop();
    }
}

Ethernet.init() はイーサネットシールドとのSPI通信につかうCSピンです。
server.available() でクライアントの接続を待ちます。
client.available() で受信したByte数を取得してそのByte数だけclient.read()で読み出します。



その他参考




TCPとUDPの違い

TCP
送信したパケットは勝手に分割されて適当に結合されて受信される(データの境界保証なし)
つまり1回send() ≠1回recv()

送信したデータが受信されないと送信側はブロッキングする。
つまり、通信状況が悪いと送信タイミングのリアルタイム性が損なわれる。




UDP
送信したパケットは勝手に分割されないが、あまりデータが大きいとIPフラグメンテーションされる可能性がある。(パケット分割とIPフラグメントは別物らしい)
その場合受信側でデータがすべて出揃うまで受信を完了しない。(データの境界保証あり)
つまり1回send() =1回recv()
また、データがいつまでたっても揃わなかったらそのデータは破棄される。

send()したデータの順番とrecv()したデータの順番が入れ替わる可能性がある。
送信側は受信側がちゃんと受信できたか確認しないで構わず送信し続けるため、
通信状況が悪くても送信タイミングのリアルタイム性が損なわれない。




IPフラグメンテーション