m_shige1979のささやかな抵抗と欲望の日々

プログラムの勉強をしながら学習したことや経験したことをぼそぼそと書いていきます

Github(変なおっさんの顔でるので気をつけてね)

https://github.com/mshige1979

go言語の学習:websocketで画像を送信し、webで表示

できたの


なにこれ?

websocketサーバより1秒単位で画像をバイナリ形式で送信したもの

ソース(サーバ側)

package main

import (
	"log"
	"net/http"
	"golang.org/x/net/websocket"
	"time"
	"io/ioutil"
	"image"
	"gocv.io/x/gocv"
	"image/color"
	"bytes"
	"image/jpeg"
	"os/user"
)

var (
	black = color.RGBA{0, 0, 0, 0}
)

func main() {
	name := "server"
	log.Println(name + " start")

	http.Handle("/ws", websocket.Handler(WsHandler))
	err := http.ListenAndServe(":8001", nil)
	if err != nil {
		panic("ListenAndServe: " + err.Error())
	}

	log.Println(name + " end")
}

func WsHandler(ws *websocket.Conn) {
	log.Println("connect")

	// 自動送信
	go autoSend(ws)

	// 受信処理監視
	for {
		var b []byte
		err := websocket.Message.Receive(ws, &b)
		if err != nil {
			log.Println(err)
			break
		}
	}
}

// 任意の画像を送信する
func autoSend(ws *websocket.Conn) {
	t := time.NewTicker(1 * time.Second)
	for {
		select {
			case <-t.C:
				// ユーザディレクトリ取得
				user, err := user.Current()
				if err != nil {
					log.Println(err)
					return
				}

				// 画像ファイル読み込み
				b, err := ioutil.ReadFile(user.HomeDir + "/Desktop/" + "pug3_pic1.jpg")
				if err != nil {
					break
				}

				// 画像変換
				atom, err := gocv.IMDecode(b, gocv.IMReadUnchanged)
				if err != nil {
					log.Println(err)
					return
				}
				defer atom.Close()

				// 日時取得
				now := time.Now()

				// 偶数
				if int(now.Second()) % 2 == 0 {
					gocv.Rectangle(&atom, image.Rect(400, 400, 900, 700), black, 2)
				} else {
					gocv.Rectangle(&atom, image.Rect(400, 400, 900, 700), black, -1)
				}

				// 文字列を出力
				timeStr := now.Format("2006/01/02 15:04:05")
				gocv.PutText(&atom, timeStr, image.Pt(20, atom.Rows() - 40), gocv.FontHersheyComplex, 1, black, 1)

				// 画像に変換
				image, err := atom.ToImage()
				if err != nil {
					log.Println(err)
					return
				}
				var jpegBytes []byte
				buf := bytes.NewBuffer(jpegBytes)
				if err := jpeg.Encode(buf, image, nil); err != nil {
					log.Println(err)
				}
				b = buf.Bytes()

				websocket.Message.Send(ws, b)
				log.Println("send")
		}
	}
}

ソース(クライアント側)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <img id="img1" src="">

<script>
    var uri = "ws://localhost:8001" + "/ws";
    webSocket = new WebSocket(uri);
    webSocket.binaryType = 'blob';
    webSocket.onopen = function(e) {
        console.log("open");
    };
    webSocket.onmessage = function(e) {
        console.log("message");
        var data = e.data;
        if (data.constructor === Blob) {
            document.getElementById('img1').src = URL.createObjectURL(data);
        }
    };
    webSocket.onclose = function(e) {
        console.log("close");
    };
</script>
</body>
</html>

ソース(クライアント版2)

package main

import (
	"log"
	"golang.org/x/net/websocket"
	"strconv"
)

var (
	doneCh chan bool
	origin = "http://localhost:8001/"
	url    = "ws://localhost:8001/ws"
)

func main() {
	name := "client"
	log.Println(name + " start")

	doneCh = make(chan bool)

	ws, err := websocket.Dial(url, "", origin)
	if err != nil {
		log.Fatal(err)
		return
	}

	go receiveMsg(ws)

	<-doneCh
	log.Println(name + " end")
}

// 受信
func receiveMsg(ws *websocket.Conn) {
	var b []byte

	for {
		err := websocket.Message.Receive(ws, &b)
		if err != nil {
			log.Println(err)
			doneCh <- true
			break
		}
		log.Printf("Receive datasize=" + strconv.Itoa(len(b)))
	}
}

所感

いろいろ悩んだけど基本は画像をバイナリで送信するだけだった感じ
動画のストリーミングという感じではないのでそこまで難しくないかも…
最初はwebsocketの送受信がうまく行かなかったけどw

go言語の学習:画像を読み込み、文字を追加

日本語ができない

f:id:m_shige1979:20180729104447j:plain
時間は気にしないでwww

前のソースを一部回収

package main

import (
	"gocv.io/x/gocv"
	"log"
	"os/user"
	"time"
	"io/ioutil"
	"image/color"
	"image"
)

var (
	// サイズ
	w = 640
	h = 480

	// 色定義
	black = color.RGBA{0, 0, 0, 0}
	blue = color.RGBA{0, 0, 255, 0}
	red = color.RGBA{255, 0, 0, 0}
	white = color.RGBA{255, 255, 255, 0}
	yellow = color.RGBA{255, 255, 0, 0}
	color2 = color.RGBA{128,128,128,0}

)

func main() {

	// ユーザディレクトリ取得して保存先を作成
	user, err := user.Current()
	if err != nil {
		log.Println(err)
		return
	}

	// img読み込み
	img_file_path := user.HomeDir + "/Desktop/" + "pug3_pic1.jpg"
	// file
	b, err := ioutil.ReadFile(img_file_path)
	if err != nil {
		log.Println(err)
		return
	}

	// 画像取得
	atom, err := gocv.IMDecode(b, gocv.IMReadUnchanged)
	if err != nil {
		log.Println(err)
		return
	}
	defer atom.Close()

	// sample1描画
	sample1(&atom)

	// 領域作成
	img2 := gocv.NewMatWithSize(atom.Rows(), atom.Cols(), atom.Type())
	// 短径(線)
	gocv.Rectangle(&img2, image.Rect(400, 20, 900, 400), blue, 3)
	// 短径(塗りつぶし)
	gocv.Rectangle(&img2, image.Rect(400, 20, 900, 400), color2, -1)
	// 重ねる
	gocv.AddWeighted(atom, 1, img2, 0.5, 0, &atom)

	// 文字列を出力
	timeStr := time.Now().Format("2006/01/02 15:04:05")
	gocv.PutText(&atom, timeStr, image.Pt(20, atom.Rows() - 40), gocv.FontHersheyComplex, 1, black, 1)

	// 出力画像パス
	file_path := user.HomeDir + "/Desktop/" + time.Now().Format("20060102150405") + ".jpg"

	// 保存
	gocv.IMWrite(file_path, atom)

}

func sample1 (img *gocv.Mat) {

	// 短径描画
	gocv.Rectangle(img,image.Rect(10, 10, 100, 100), black, 5)
	gocv.Rectangle(img,image.Rect(200, 15, 300, 350), blue, 5)
	gocv.Rectangle(img,image.Rect(10, 400, 600, 470), yellow, -1) // -1の場合は塗りつぶし
	gocv.Rectangle(img,image.Rect(40, 350, 550, 450), red, -1) // -1の場合は塗りつぶし

	// 多角形描画
	ps := make([][]image.Point, 1)
	p := make([]image.Point, 4)
	p[0] = image.Pt(int(0.1*float64(w)), int(0.1*float64(h)))
	p[1] = image.Pt(int(0.1*float64(w)), int(0.4*float64(h)))
	p[2] = image.Pt(int(0.3*float64(w)), int(0.2*float64(h)))
	p[3] = image.Pt(int(0.3*float64(w)), int(0.1*float64(h)))
	ps[0] = p
	gocv.DrawContours(img, ps, 0, red, -1) // -1の場合は塗りつぶし

}

使うようになったもの

gocv.IMDecode

→ バイナリを読み込んで変換してくれる

gocv.AddWeighted

→ 既存の短径領域に別の短径領域を重ねる

gocv.PutText

→ 指定の場所にテキストを追加

所感

IDEってやっぱり良いです\(^o^)/
viなどでもなんかできそうですけど、commandキーや入力している間に引数やメソッドの候補がでるのは嬉しい

go言語の学習:gocvで画像を作成して出力

gocv

Home :: GoCV - Golang Computer Vision Using OpenCV 3
opencvでなんかいろいろやってくれるライブラリ

インストールするまでくっそ面倒

macの場合はbrew installで簡単そうですけど
なんかいろいろエラーでて大変でした
pythonいれたり、クリーンアップしたり、brew docterして問題解決したり色々やったなあ…
・多分、最新バージョンならwindowsでもできると思います

ソース

package main

import (
	"image"
	"image/color"

	"gocv.io/x/gocv"
	"log"
	"os/user"
	"time"
	"os"
	"image/jpeg"
)

var w = 640
var h = 480

func main() {
	// 描画領域作成
	atom := gocv.NewMatWithSize(h, w, gocv.MatTypeCV8UC3)
	defer atom.Close()

	// 色定義
	black := color.RGBA{0, 0, 0, 0}
	blue := color.RGBA{0, 0, 255, 0}
	red := color.RGBA{255, 0, 0, 0}
	white := color.RGBA{255, 255, 255, 0}
	yellow := color.RGBA{255, 255, 0, 0}

	// 短径描画
	gocv.Rectangle(&atom,image.Rect(0, 0, 640, 480), white, -1)
	gocv.Rectangle(&atom,image.Rect(10, 10, 100, 100), black, 5)
	gocv.Rectangle(&atom,image.Rect(200, 15, 300, 350), blue, 5)
	gocv.Rectangle(&atom,image.Rect(10, 400, 600, 470), yellow, -1) // -1の場合は塗りつぶし
	gocv.Rectangle(&atom,image.Rect(40, 350, 550, 450), red, -1) // -1の場合は塗りつぶし

	// 多角形描画
	ps := make([][]image.Point, 1)
	p := make([]image.Point, 4)
	p[0] = image.Pt(int(0.1*float64(w)), int(0.1*float64(h)))
	p[1] = image.Pt(int(0.1*float64(w)), int(0.4*float64(h)))
	p[2] = image.Pt(int(0.3*float64(w)), int(0.2*float64(h)))
	p[3] = image.Pt(int(0.3*float64(w)), int(0.1*float64(h)))
	ps[0] = p
	gocv.DrawContours(&atom, ps, 0, red, -1) // -1の場合は塗りつぶし

	// ユーザディレクトリ取得して保存先を作成
	user, err := user.Current()
	if err != nil {
		log.Println(err)
		return
	}
	file_path := user.HomeDir + "/Desktop/" + time.Now().Format("20060102150405") + ".jpg"

	// 保存
	//gocv.IMWrite(file_path, atom)

	// 画像に変換
	image, err := atom.ToImage()
	if err != nil {
		log.Println(err)
		return
	}

	// file出力
	file, errOs := os.Create(file_path)
	if errOs != nil {
		log.Println(err)
		return
	}
	defer file.Close()

	// jpeg形式で出力
	if err := jpeg.Encode(file, image, &jpeg.Options{100}); err != nil {
		log.Println(err)
		return
	}

	/*
	// bytes変換
	var jpegBytes []byte
	buf := bytes.NewBuffer(jpegBytes)
	if err := jpeg.Encode(buf, image, nil); err != nil {
		log.Println(err)
	}
	jpegBytes = buf.Bytes()
	*/

}

できたもの(適当)
f:id:m_shige1979:20180728200639j:plain

本来は

webカメラの情報をキャプチャして取得するとかもできますけど
持っているwebカメラではだけでした(´・ω・`)

所感

複雑な画像編集することが容易にできるような感じとのこと
文字入れたり、グレースケールとかできたらやってみたい

go言語の学習:構造体よりタグ情報を取得

構造体にあるjsonなどのタグより取得

package main

import (
	"fmt"
	"reflect"
)

type Sample struct {
	no    int     `sampletag:"aaaaa"`
	name  string  `sampletag:"bbbbb"`
	age   int     `sampletag:"ccccc"`
}

func main() {
	fmt.Println("start")

	// Sample1インターフェースを定義
	var sample1 Sample

	// 型の取得
	t := reflect.TypeOf(sample1)

	// タグの情報を取得
	for i:=0;i<t.NumField();i++ {
		item := t.Field(i)
		fmt.Printf("index: %2d, name:%10s, type:%10s, tag:%10s\n",
			i, item.Name, item.Type.Name(), item.Tag.Get("sampletag"))
	}

	fmt.Println("end")
}

start
index:  0, name:        no, type:       int, tag:     aaaaa
index:  1, name:      name, type:    string, tag:     bbbbb
index:  2, name:       age, type:       int, tag:     ccccc
end

DBのORMやjsonエンコード、デコードなどで使用されている
タグ情報を設定することで構造体の項目のバリデーションなどにも併用できる感じ…

go言語の学習:ポリモーフィズムのパターン

Javaとかではクラスにinterfaceとか定義して使用する

goの場合は構造体自体はメソッドを実装するが、定義されているインターフェースは定義しない

サンプル
package main

import (
	"fmt"
)


// Sample1インターフェース、メソッドを定義
type Sample1 interface {
	Print()
}

// Hoge構造体、Printメソッドを用意
type Hoge struct {
	a int
}
func (c Hoge) Print() {
	fmt.Println("hoeghoge")
	fmt.Println(c.a)
}

// Foo構造体、Printメソッドを用意
type Foo struct {
	a int
}
func (c Foo) Print() {
	fmt.Println("foofoo")
	fmt.Println(c.a)
}

func main() {
	fmt.Println("start")

	// Sample1インターフェースを定義
	var sample1 Sample1

	// 構造体を生成
	hoge := Hoge{a: 100}
	foo  := Foo{a: 500}

	// sample1変数にHoge構造体を設定して実行
	sample1 = hoge
	sample1.Print()

	// sample1変数に Foo構造体を設定して実行
	sample1 = foo
	sample1.Print()

	fmt.Println("end")
}

interface型に共通の呼び出したいメソッドを定義しておくことで実行することが可能
しかし、この場合はSample1を定義しておかないといけない

var sample1 interface{}

のように定義していないinterfaceを使用した場合は動かすことができるか?

typeで定義されたメソッドを使用前に定義することで
対応は可能、しかし、設定された変数には定義されていないこともあるため、型アサーションで存在チェックすることが必要

	// Sample1インターフェース、メソッドを定義
	type Sample1 interface {
		Print()
	}

	// sample1変数にHoge構造体を設定して実行
	sample1 = hoge
	a, ok := sample1.(Sample1)
	if !ok {
		fmt.Println("Print実行不可")
		return
	}
	a.Print()

所感

まああれかな、JavaでObject型で受け取ってどのクラス情報を保持するかみたいな感じと思います。
今回でいうとinterface型で定義して受け取って入れば対象のinterfaceのメソッドに応じて処理を切り替えられる感じ
interface型の仕組みを上手く理解していないのでこのあたり考えて対応する必要がありそうです。

go言語の学習:interface型の値設定について

interface型は基本的にはどんな値も設定できる
package main

import (
	"fmt"
)

func main() {
	fmt.Println("start")

	var a interface{}

	a = int(10)
	a = int64(10)
	a = string(65)

	fmt.Printf("%+v\n", a)
	fmt.Println("end")
}

設定はできる…

しかし、そのままでは文字列連結や加算、減算などはできないようになっている感じ

型変換
package main

import (
	"fmt"
)

func main() {
	fmt.Println("start")

	var a interface{}

	a = int(10)
	a = a.(int) * 100

	fmt.Printf("%+v\n", a)
	fmt.Println("end")
}

「変数.(設定されている値のデータ型)」でその値へ変換してくれます

リフレクションでinterfaceに設定されたデータ型を確認できる
package main

import (
	"fmt"
	"reflect"
)

func main() {
	fmt.Println("start")

	var a interface{}
	var v reflect.Value

	a = int(10)
	v = reflect.ValueOf(a)
	fmt.Printf("a(type): %+v\n", v.Type())

	a = int64(100)
	v = reflect.ValueOf(a)
	fmt.Printf("a(type): %+v\n", v.Type())

	a = "aaaaaaaaaa"
	v = reflect.ValueOf(a)
	fmt.Printf("a(type): %+v\n", v.Type())

	fmt.Printf("%+v\n", a)
	fmt.Println("end")
}

start
a(type): int
a(type): int64
a(type): string
aaaaaaaaaa
end

まとめ

構造体とか定義せずにいい感じのデータ型が混ざったやつって定義したいなぁぁぁぁ

go言語の学習:typeでインターフェースを定義

概要

interface自体はstructを利用してする感じと認識している
構造体とかとなんかごっちゃになっていてJavaのクラスとかと勝手が異なるので混乱している

そもそもstringやintにメソッドは付与できるのか?

type data1 string
type data2 int

こんな感じで定義するらしい
関数?メソッド?を入れたい場合は構造体でクラスを定義したときと同じ感じでいける

サンプル

package main

import (
	"fmt"
)

func main() {
	fmt.Println("start")

	var a data1
	a = "aaaaaaa"
	a.test1()
	a.test2()
	a.clear()
	fmt.Println("sample1:" + a)

	var b data2
	b = 50
	b.add()
	fmt.Printf("sample2:%d\n", b)

	fmt.Println("end")
}

// dataというインターフェースの定義
type data1 string
type data2 int

// サンプルメソッド
func(c *data1) test1() {
	fmt.Println("test1:" + *c)
}

// サンプルメソッド
func(c *data1) test2() {
	fmt.Println("test2: [" + *c + "]")
}

// サンプルメソッド
func(c *data1) clear() {
	*c = ""
}

// サンプルメソッド
func (c *data2) add() {
	*c *= 100
}

インターフェースという考えがうまく理解できていないのでこのような
記載はあまりできなかった(´・ω・`)

まとめ

typeを使用してインターフェース型として定義すれば
string、intでメソッドを付与できる
わざわざ構造体を定義したくない場合はこちらで対応可能