DRYな備忘録

Don't Repeat Yourself.

How to install gosseract to CentOS 7

What is this document for?

"gosseract" is a Tesseract-OCR wrapper for Golang, and this document is for an issue reported to "gosseract"

github.com

Reproduce the issue

Set up environment for simulating CentOS

# because I'm using MacOS
% docker-machine create -d virtualbox goss-test
% eval $(docker-machine env goss-test)
% docker run --rm -t -i library/centos:centos7

##
# Dive into CentOS
##

try to install Golang itself

[root@2bc64375ee2c /]# yum search go | grep golang
[root@2bc64375ee2c /]# yum install -y golang
[root@2bc64375ee2c /]# go version
go version go1.6.3 linux/amd64
# successfully

try to get gosseract

[root@2bc64375ee2c /]# go get github.com/otiai10/gosseract
package github.com/otiai10/gosseract: cannot download, $GOPATH not set. For more details see: go help gopath

OK, set $GOPATH

[root@2bc64375ee2c /]# export GOPATH=/

[root@2bc64375ee2c /]# go get github.com/otiai10/gosseract
go: missing Git command. See https://golang.org/s/gogetcmd
package github.com/otiai10/gosseract: exec: "git": executable file not found in $PATH

OK, install git

[root@2bc64375ee2c /]# yum install -y git

[root@2bc64375ee2c /]# go get github.com/otiai10/gosseract
go build github.com/otiai10/gosseract/tesseract: g++: exec: "g++": executable file not found in $PATH

OK, install g++

[root@2bc64375ee2c /]# yum install -y gcc-c++
[root@2bc64375ee2c /]# go get github.com/otiai10/gosseract
# github.com/otiai10/gosseract/tesseract
src/github.com/otiai10/gosseract/tesseract/tess.cpp:1:31: fatal error: tesseract/baseapi.h: No such file or directory
 #include <tesseract/baseapi.h>
                               ^
compilation terminated.
[root@2bc64375ee2c /]#

OK, the problem is reproduced now

Solution

Install tesseract-ocr.

for preparation

[root@2bc64375ee2c /]# yum install -y autoconf automake libtool
[root@2bc64375ee2c /]# yum install -y libjpeg-devel libpng-devel libtiff-devel zlib-devel
[root@2bc64375ee2c /]# wget http://www.leptonica.org/source/leptonica-1.72.tar.gz
bash: wget: command not found
[root@2bc64375ee2c /]# yum install -y wget

install leptonica

[root@2bc64375ee2c /]# wget http://www.leptonica.org/source/leptonica-1.72.tar.gz
[root@2bc64375ee2c /]# tar -xzvf leptonica-1.72.tar.gz
[root@2bc64375ee2c /]# cd leptonica-1.72
[root@2bc64375ee2c leptonica-1.72]# ./configure
[root@2bc64375ee2c leptonica-1.72]# make
[root@2bc64375ee2c leptonica-1.72]# make install

# ...
----------------------------------------------------------------------
Libraries have been installed in:
   /usr/local/lib

If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR`
flag during linking and do at least one of the following:
   - add LIBDIR to the `LD_LIBRARY_PATH` environment variable
     during execution
   - add LIBDIR to the `LD_RUN_PATH` environment variable
     during linking
   - use the `-Wl,-rpath -Wl,LIBDIR` linker flag
   - have your system administrator add LIBDIR to `/etc/ld.so.conf`

See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------
# ...

[root@2bc64375ee2c leptonica-1.72]#
[root@2bc64375ee2c leptonica-1.72]# cd ..

install tesseract

[root@2bc64375ee2c /]# wget https://github.com/tesseract-ocr/tesseract/archive/3.02.02.tar.gz
[root@2bc64375ee2c /]# tar -xzvf 3.02.02.tar.gz
[root@2bc64375ee2c /]# cd tesseract-3.02.02/
[root@2bc64375ee2c tesseract-3.02.02]# ./autogen.sh
[root@2bc64375ee2c tesseract-3.02.02]# ./configure
[root@2bc64375ee2c tesseract-3.02.02]# make
[root@2bc64375ee2c tesseract-3.02.02]# make install

# ...
----------------------------------------------------------------------
Libraries have been installed in:
   /usr/local/lib

If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR`
flag during linking and do at least one of the following:
   - add LIBDIR to the `LD_LIBRARY_PATH` environment variable
     during execution
   - add LIBDIR to the `LD_RUN_PATH` environment variable
     during linking
   - use the `-Wl,-rpath -Wl,LIBDIR` linker flag
   - have your system administrator add LIBDIR to `/etc/ld.so.conf`

See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------
# ...
[root@2bc64375ee2c tesseract-3.02.02]# tesseract --version
tesseract 3.02.02
 leptonica-1.72
  libjpeg 6b (libjpeg-turbo 1.2.90) : libpng 1.5.13 : libtiff 4.0.3 : zlib 1.2.7

[root@2bc64375ee2c tesseract-3.02.02]#

tesseract-ocr is successfully installed. It's better to locate tessdata of any language, but skip now.

[root@2bc64375ee2c /]# cd $GOPATH/src/github.com/otiai10/gosseract/
[root@2bc64375ee2c gosseract]# go test
# github.com/otiai10/gosseract
all_test.go:12:2: cannot find package "github.com/otiai10/mint" in any of:
    /usr/lib/golang/src/github.com/otiai10/mint (from $GOROOT)
    /src/github.com/otiai10/mint (from $GOPATH)
FAIL    github.com/otiai10/gosseract [setup failed]
[root@2bc64375ee2c gosseract]# go get -u github.com/otiai10/mint
[root@2bc64375ee2c gosseract]# go test
/tmp/go-build910821104/github.com/otiai10/gosseract/_test/gosseract.test: error while loading shared libraries: liblept.so.4: cannot open shared object file: No such file or directory
exit status 127
FAIL    github.com/otiai10/gosseract    0.002s
[root@2bc64375ee2c gosseract]#

# OK, now facing new problem

check files in /usr/local/lib

[root@2bc64375ee2c gosseract]# ls /usr/local/lib/liblept.so.4
/usr/local/lib/liblept.so.4

# Yes, there is

need to add path to shared library

[root@2bc64375ee2c gosseract]# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
[root@2bc64375ee2c gosseract]# go test
Error opening data file /usr/local/share/tessdata/eng.traineddata
Please make sure the TESSDATA_PREFIX environment variable is set to the parent directory of your "tessdata" directory.
Failed loading language 'eng'
Tesseract could not load any languages!
Could not initialize tesseract.
exit status 1
FAIL    github.com/otiai10/gosseract    0.006s

OK, it's almost there. Now we need traineddata for eng.

[root@2bc64375ee2c gosseract]# wget https://github.com/tesseract-ocr/tessdata/raw/master/eng.traineddata
[root@2bc64375ee2c gosseract]# mv eng.traineddata /usr/local/share/tessdata/
[root@2bc64375ee2c gosseract]# go test
PASS
ok      github.com/otiai10/gosseract    0.278s
[root@2bc64375ee2c gosseract]#

Yay!

refs

DRYな備忘録として

【iOS】プロジェクト内に配置したjsonファイルの内容をSwiftyJSONで取得する

let filepath = NSBundle.mainBundle().pathForResource("message", ofType:"json")
// 1. Resourcesっていうグループ(と物理ディレクトリ)作ってるけど、ファイル名だけでよい
// 2. 返り値はnullable(String?)なので注意

let data = NSData(contentsOfFile: filepath!)
let json = JSON(data: data!)
// SwiftyJSON.JSONのイニシャライザがいろいろあって、
// `data:`を明示しないとNSDataをとるイニシャライザが発火してなかったみたいで、5分ぐらい詰まった

// たとえばtop levelがarrayのJSONだったら
print(json.arrayValue.count)

適宜guardするなりしてください

DRYな備忘録として

【Go言語】GoでJWT(JSON Web Token)を使うサンプル

参考

package main

import (
    "log"

    jwt "github.com/dgrijalva/jwt-go"
)

type User struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
    jwt.StandardClaims
}

func createTokenString() string {
    // User情報をtokenに込める
    token := jwt.NewWithClaims(jwt.GetSigningMethod("HS256"), &User{
        Name: "otiai10",
        Age:  30,
    })
    // Secretで文字列にする. このSecretはサーバだけが知っている
    tokenstring, err := token.SignedString([]byte("foobar"))
    if err != nil {
        log.Fatalln(err)
    }
    return tokenstring
}

func main() {
    // たとえばリクエストのヘダーからトークン文字列を受け取ったとする
    tokenstring := createTokenString()
    // これね
    log.Println(tokenstring)
    // サーバだけが知り得るSecretでこれをParseする
    token, err := jwt.Parse(tokenstring, func(token *jwt.Token) (interface{}, error) {
        return []byte("foobar"), nil
    })
    // Parseメソッドを使うと、Claimsはmapとして得られる
    log.Println(token.Claims, err)

    // 別例, jwt.StandardClaimsを満たすstructに直接decodeさせることもできる
    user := User{}
    token, err = jwt.ParseWithClaims(tokenstring, &user, func(token *jwt.Token) (interface{}, error) {
        return []byte("foobar"), nil
    })
    log.Println(token.Valid, user, err)
}

gistのほう Golang JWT Example (2017/Oct/26) · GitHub

DRY

みんなのGo言語【現場で使える実践テクニック】

みんなのGo言語【現場で使える実践テクニック】

【iOS】StackViewで子供のViewを3分の1幅にしたい【AutoLayout】

ゴール

  • StackView(やAutoLayout)を使っていて、1/3分割にしたいときがある
  • Multiplierに整数や小数は入れたことあるけど、分数(無限分数)はどうやって入れるんだ?

f:id:otiai10:20160925025835p:plain

調査

stackoverflow.com

解決

結局、CGFloatへのalias表現みたいなものなので、3つとも「1:3」を指定してしまうと警告が出る。なので、たとえば真ん中は指定せず(Super ViewとしてStackViewを使っているなら「残りの幅をうめる」)とするのが良いっぽい。

雑感

【Xcode】あると思うんだけどno matching provisioning profiles foundとか言われる

stackoverflow.com

  1. Preference
  2. Accounts
  3. View Details
  4. 今あるやつ全部捨てる(右クリック)
  5. Xcodeを殺す(← ここ重要)
  6. 再起動
  7. Preference > Accounts > View Details で、「Download All」

Fabric/Crashlyticsで同プロジェクトの別bundle identifierのアプリを追加する

twittercommunity.com

  1. Xcodeにおいてbundle identifierを変更する
  2. Fabricデスクトップアプリにおいて「+ New App」というボタンがあるのでクリック
  3. 同プロジェクトを選択する
  4. チュートリアルが始まるが、だいたいのことは済んでるので、⌘+Bとか適当にしてチュートリアルをこなす
  5. おわり

【iOS】画面の向き(Orientation)を特定のページのみで制限したり許可したりしたい【supportedInterfaceOrientations】

ゴール

たとえば

  • 基本的にPortrait(縦向き)のみに制限したいんだけど、特定の画面だけではLandscape(横向き)を許可したい

特定のViewで、強制的に向きを変えることはできる

けど、これは向きを変えるだけであって、ふたたび端末をぐるっとすると縦向きに戻ったりする。かなしい。

    override func viewDidLoad() {
        super.viewDidLoad()
        // Force Orientation Landscape
        UIDevice.currentDevice().setValue(UIInterfaceOrientation.LandscapeLeft.rawValue, forKey: "orientation")
    }

Info.plistで、全体的に使うOrientationは制限できる

プロジェクトファイル(file path的にはInfo.plist)で、以下のような項目があるから「これでいーじゃーん」と思ってチェックしても、これはアプリケーション全体で許可非許可の扱いなので、「◯◯というViewだけは横ね」みたいにホワイトリスト的な挙動は設定できない。

f:id:otiai10:20160919045040p:plain

supportedInterfaceOrientationsを使う

ViewControllerのメソッド(shouldAutoRotateと)supportedInterfaceOrientationを使えば、ViewControllerごとにサポートするOrientationを明示的に指定できる。

class MyViewController: UIViewController {

    // ...

    // ここでtrueを返すと、`supportedInterfaceOrientations`が呼ばれる
    override func shouldAutorotate() -> Bool {
        // まあでもデフォルトでtrue返すっぽいし、overrideしなくていいっぽい
        return true
    }

    // ここでサポートできるOrientationを明示できる
    override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.Portrait
    }
}

supportedInterfaceOrientationsがなんかうまく動かないときに疑うこと

しかし、これだけだと(だいたいの場合)動かない(と思う)。なぜなら、

When the user changes the device orientation, the system calls this method on the root view controller or the topmost presented view controller that fills the window. supportedInterfaceOrientations #Discussion

「ユーザがデバイスの向きを変えたとき、システムはルートのViewControllerもしくは一番上で画面いっぱいに表示されているViewControllerの supportedInterfaceOrientationsを呼びます。」

とのことで、だいたいの場合、UINavigationControllerとか、TabBarControllerとか、PageViewControllerとかにこれを実装するハメになってくるんではないかと思う。

UINavigationControllerを継承したMyNavigationControllerとかを作ってもいいんですけど

一案として、上記のメソッドを実装した独自のMyNavigationControllerを作って、下記のようにするのかもしれない。

class MyNavigationController: UINavigationController {
    override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.Portrait
    }
}
// で、Storyboardなりで、起点になるNavigationControllerの
// classをこいつに指定してあげれば、このメソッドが呼ばれるはず

たぶんこれでも動くんだけど、若干のだるさがあるので、標準のUINavigationControllerのextension作っちゃえばいいじゃないか、という結論になった。

最終的に

// TabBarControllerのcontextがメイン
extension UITabBarController {
    override public func shouldAutorotate() -> Bool {
        return true
    }
    // もうちょっとちゃんとやる場合は、子供のViewControllerに
    // supportedInterfaceOrientationsを実装して
    // return selectedVC.supportedInterfaceOrientations()
    // とかすれば、判断を子供に委譲できる
    override public func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        guard let selectedVC = self.selectedViewController else {
            return UIInterfaceOrientationMask.Portrait
        }
        // TabBarControllerの中でNavigationを使ってるケースがある
        guard let navigation = selectedVC as? UINavigationController else {
            return UIInterfaceOrientationMask.Portrait
        }
        // Navigationで一番うえのやつを取得して
        guard let current = navigation.viewControllers.last else {
            return UIInterfaceOrientationMask.Portrait
        }
        // これ!このビューのときだけ横を許可して!!
        if current is MyCoolViewController {
            return UIInterfaceOrientationMask.Landscape
        }
        return UIInterfaceOrientationMask.Portrait
    }
}

// サービスの登録フローとかは、いきなりNavigationが出るので
extension UINavigationController {
    override public func shouldAutorotate() -> Bool {
        return true
    }
    override public func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.Portrait
    }
}

// サービスのイントロ画面は、いきなりPageViewControllerなので
extension UIPageViewController {
    override public func shouldAutorotate() -> Bool {
        return true
    }
    override public func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.Portrait
    }
}

という感じになった。これを、AppDelegate.swiftのケツにでも追加しとく。今回は明示的に "ここでだけで" まとめたかったので、各々のsupportedInterfaceOrientationを呼ぶ実装にはしなかったけど、必ず各々に判断を委譲するほうが綺麗なケースもあると思う。

雑感

  • swiftのコードをインターネッツでググっても、動いたり動かなかったりする
  • swiftという言語が若いからかなと思ったけど、どうやらそうでもない
    • 情報の絶対量はAndroidよりも多いし
    • Objective-Cの情報でも参考にできることは多いし
  • おそらくアプリの要件が人それぞれで、しかもそれがけっこう切り分けられないんだと思われる
    • 今回のケースでいうとroot view controllerがTabBarControllerだったり、NavigationControllerだったり、PageViewControllerだったりした
    • とかそういうこと
  • であるからして、慌てず焦らず、ちゃんと英語読んで、公式も当たって「なぜ動かないか」を環境非依存な知識までかみ砕いて理解していくのが大事だなあ、と思った
  • 勉強になります

DRYな備忘録として