読者です 読者をやめる 読者になる 読者になる

Septeni Engineer's Blog

セプテーニエンジニアが綴る技術ブログ

gradle + scalariformでbuild時に自動フォーマット

はじめに

お久しぶりです、加藤です。
そろそろ夏の訪れを感じる頃かと思います。 最近、業務でscalariformを導入する機会があったため、その手順を書いていこうかなと思います。 sbt + scalariformも導入しましたが、こちらは参考記事がたくさんあるので割愛します。

手順

  • プロジェクト内のbuild.gradleに追記
  • フォーマッターの設定ファイル(scalariform.properties)を作成

では、実際に記述していきます。

続きを読む

Fluentdを使ってツイートをSlackに流す

こんにちは、丸山です。

ご存知の方も多いと思いますが、Fluentdというログ収取用のミドルウェアがあります。その特徴の一つにプラグインによる様々な形でのログ収集というものがあります。プラグインの組み合わせによっては、ログだけではなく、様々なメッセージを収集し、別のところへ転送するといった連携を行うことが可能です。

そこで今回はtwitterとSlackに対応したプラグインを使用して、ツイートをSlackに流してみます。

Fluentdのインストール(Mac OS X

ここからdmgファイルをダウンロードしてインストールします。

デフォルトでは設定ファイルの場所は/etc/td-agent/td-agent.conf、ログの場所は/var/log/td-agent/td-agent.logになります。

プラグインのインストール

次に各プラグインをインストールします。

以下は今回インストールするプラグインとそれぞれのインストール方法です。

fluent-plugin-twitter

sudo td-agent-gem install eventmachine
sudo td-agent-gem install fluent-plugin-twitter

fluent-plugin-slack

sudo td-agent-gem install fluent-plugin-slack

Fluentdの設定

プラグンがインストールできたら、Fluentdの設定をしていきます。設定はconfファイルに記述していきます。

メッセージ取得元(Twitter)の設定

まずはツイートを取得する設定です。fluent-plugin-twitterTwitter Streaming APIを利用するため、ここではTwitter APIを利用する準備ができていること(consumer_key等の取得)を前提としています。

<source>
  type twitter
  consumer_key         <自分のconsumer_key>       # Required
  consumer_secret      <自分のconsumer_secret>    # Required
  oauth_token          <自分のoauth_token>        # Required
  oauth_token_secret   <自分のoauth_token_secret> # Required
  tag                  input.twitter             # Required
  timeline             tracking                  # Required (tracking or sampling or location or userstream)
  keyword              'Scala'                   # Optional (keyword has priority than follow_ids)
  lang                 ja,en                     # Optional
  output_format        nest                      # Optional (nest or flat or simple[default])
</source>

Fluentdの設定ファイルはいくつかのディレクティブを用いて記述されます。ここではsourceディレクティブを設定しています。sourceディレクティブにはログやメッセージの入力元を指定します。今回はツイートを取得してそれを他のところに流すため、メッセージ取得元であるtwitterの指定をしています。

設定内容についの詳細は省略しますが、上記の設定ではScalaというキーワードを含んだツイートを取得するようにしています。

メッセージ出力先(Slack)の設定

次にSlackへメッセージを流す設定です。fluent-plugin-slackではSlack APIのtokenが必要となりますので、予め取得しておいてください。

<match input.twitter>
  type buffered_slack
  token <Slack APIのtoken>
  channel %23fluentd-twitter
  username tweet
  icon_emoji :slack:
  flush_interval 60s
  message_keys text
  message "%s"
</match>

ここではmatchディレクティブを設定しています。matchディレクティブはログ・メッセージの出力先を指定します。今回は取得したメッセージをSlackに流すため、メッセージ出力先としてSlackの設定をしています。

上記の設定では、メッセージをtweetという名前とslackアイコンを付けて、#fluentd-twitterというチャンネルに流すようにしています。チャンネルを指定するときは、先頭に%23を付けます。

また、取得されたツイートは他の様々な情報とともにJSON形式で渡ってきます。そのなかでツイート自体はtextというkeyに対応するvalueとして渡ってくるので、message_keysにtextを指定しています。そしてmessageに"%s"と記述することで、そのtextというkeyに対応するvalue、つまりツイートをメッセージとしてSlackに表示することになります。

以上の設定を行ったら、fluentdを起動します。

sudo launchctl load /Library/LaunchDaemons/td-agent.plist

ちなみに停止は以下のコマンドです。

sudo launchctl unload /Library/LaunchDaemons/td-agent.plist

これで取得対象のツイートがされると、Slackにそのツイートが流れるようになります。

post-checkoutフックを使ってブランチ切り替え時にちょっとだけ自動でDB環境をセットアップできるようにした

こんにちは、杉山です。

最近、ブランチ切り替えの度にDBの構造が違ってたりするので、切り替えるたびにDBマイグレーションしたりダンプとって読み込んだりとかいうことをやってました。手動でやるのがそろそろだるくなってきたのでこれを機に自動化したいと思います。

続きを読む

ECSのcloudformationのtemplateを作成

こんにちは、貴子です。
dockerを使ったコンテナ化をしたいというプロジェクトがあったので、ECSの導入検討をしました。
その際に作成したcloudformationのtemplateを載せます。

ECSを選んだ理由は?

AWSでdockerのサービスは2つ、AWS Elastic BeanstalkとECSがあります。
何がどう違うの?というところが解らなかったので、AWSのソリューションアーキテクトの方に弊社へ来ていただいてサービスのご紹介を頂きました。
Beanstalkの中でECSが動いているので大きな違いはありませんが、Beanstalkの方は元々PaaSなので、色々元々組み込まれて便利に使え、.ebextensionsというYAMLで管理できるようになっていいます。
ECSはdockerに特化したもので、最新機能はECSの方が組み込まれるのが早いそうです。
.ebextensionsを使っていくと何でもできちゃうので運用めっちゃ大変そうだなーと思って、シンプルにECSを試しました。

ECSについては、公式サイトの紹介の動画 Amazon EC2 Container Service (Docker コンテナ管理) | AWS が解りやすいです。

ECSのtemplate

cloudformation

弊社ではcloudformationは1枚のjsonファイルではなく、base.template・dev.template・dev.elb.template・staging.template…とのように分けて作って管理しているので、ecsのtemplateも同様に管理します。 cloudformationのstack policyは、意図せずに削除とかリプレースがかかると嫌なので、基本的にそれをDenyにするものを設定しています。
全て同じtemplateに書くとdevのインスタンスをリプレースしたいだけなのに、
productionやstagingのstack policyも変わってしまうので、それを避けるためにある程度分けて管理しています。

ECSのtemplate

VPCやSubnet・InternetGateway等は作成済みとします。

1. ecs.cluster.template

ECSのClusterの登録です。今回はdevサーバーを作成し、そこにnginxのコンテナを起動させます。

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "ecs cluster stack",
  "Resources" : {
    "Dev01": {
      "Type": "AWS::ECS::Cluster"
    }
  }
}
2. ecs.task.definitions.template

TaskDefinitions = コンテナ情報を登録します。
docker hubからの取得ではなく、弊社で立てたPrivateのdocker registryから取得します。
ECRも出ましたが、オレゴンバージニアアイルランドに繋ぎに行くよりかは
東京リージョン内に自前で立てたほうが早かったので、こちらにしました。

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "ecs task stack",
  "Parameters" : {
    "NginxContainerName" : {
      "Type" : "String",
      "Default": "nginx"
    },
    "NginxImage" : {
      "Type" : "String",
      "Default": "【Private docker registryドメイン】/【コンテナ名】:【コンテナバージョン】"
    },
  "Resources" : {
    "Nginx": {
      "Type": "AWS::ECS::TaskDefinition",
      "Properties" : {
        "ContainerDefinitions" : [ {
          "Name": { "Ref" : "NginxContainerName" },
          "Image": { "Ref" : "NginxImage" },
          "PortMappings":[ {
            "ContainerPort": 80,
            "HostPort": 80
          } ],
         "Cpu": "1024",
         "Memory":"500"
        } ]
      }
    }
  }
}
3. ecs.role.template

EC2インスタンスとECSのIAMを登録します。
ECSがEC2インスタンスを利用するので、EC2インスタンスにもIAMが必要と思っていなくて、結構躓きました。  

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "ecs iam role stack",
  "Resources" : {
    "EC2Role": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [ {
            "Effect": "Allow",
            "Principal": {
              "Service": [ "ec2.amazonaws.com" ]
            },
            "Action": [ "sts:AssumeRole" ]
          } ]
        },
        "Path": "/",
        "Policies": [ {
          "PolicyName": "ecs-service",
          "PolicyDocument": {
            "Statement": [ {
              "Effect": "Allow",
              "Action": [
                "ecs:CreateCluster",
                "ecs:DeregisterContainerInstance",
                "ecs:DiscoverPollEndpoint",
                "ecs:Poll",
                "ecs:RegisterContainerInstance",
                "ecs:StartTelemetrySession",
                "ecs:Submit*",
                "ecr:GetAuthorizationToken",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage"
              ],
              "Resource": "*"
            } ]
          }
        } ]
      }
    },
    "ECSServiceRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [ {
            "Effect": "Allow",
              "Principal": {
                "Service": [ "ecs.amazonaws.com" ]
              },
              "Action": [ "sts:AssumeRole" ]
            }
          ]
        },
        "Path": "/",
        "Policies": [ {
          "PolicyName": "ecs-service",
          "PolicyDocument": {
            "Statement": [ {
               "Effect": "Allow",
               "Action": [
                 "ec2:AuthorizeSecurityGroupIngress",
                 "ec2:Describe*",
                 "elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
                 "elasticloadbalancing:Describe*",
                 "elasticloadbalancing:RegisterInstancesWithLoadBalancer"
               ],
               "Resource": "*"
            } ]
          }
        } ]
      }
    }
  }
}
4. ecs.dev01.template

インスタンスの作成には2つポイントがあります。
 (a).まずECS用のAMIを使わなければいけないこと
 (b).UserDataで ECS_CLUSTERの名前を設定しなくてはいけないこと
ずっとUserDataをdefaultで作成を試していて、起動してもECSのコンソール画面に表示されなくて、何故なのかを見つけるのが大変でした。。

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "dev ecs-ec2 stack",
  "Parameters" : {
    "AvailabilityZone" : {
      "Type" : "AWS::EC2::AvailabilityZone::Name",
      "Default": "ap-northeast-1c"
    },
    "ImageId" : {
      "Type" : "AWS::EC2::Image::Id",
      "Default": "ami-065a6b68"
    },
    "InstanceType" : {
      "Type" : "String",
      "Default": "t2.medium"
    },
    "KeyName" : {
      "Type" : "AWS::EC2::KeyPair::KeyName",
      "Default": "【key name】"
    },
    "SecurityGroupId" : {
      "Type" : "CommaDelimitedList",
      "Default": "【セキュリティグループ id】"
    },
    "SubnetId" : {
      "Type" : "AWS::EC2::Subnet::Id",
      "Default": "【サブネット】"
    },
    "ECSCluster" : {
      "Type" : "String",
      "Default": "【クラスター名】"
    },
    "EC2Role" : {
      "Type" : "String",
      "Default": "【3で作ったEC2 Role】"
    }
  },
  "Resources" : {
    "DevInstance" : {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "AvailabilityZone" : { "Ref" : "AvailabilityZone" },
        "ImageId" : { "Ref" : "ImageId" },
        "InstanceType" : { "Ref" : "InstanceType" },
        "KeyName" : { "Ref" : "KeyName" },
        "BlockDeviceMappings" : [{ "DeviceName" : "/dev/xvda" , "Ebs" : { "DeleteOnTermination" : "false" , "VolumeSize" : "100" }}],
        "InstanceInitiatedShutdownBehavior" : "stop",
        "Monitoring" : "true",
        "SourceDestCheck" : "true",
        "UserData" : { "Fn::Base64" : { "Fn::Join": [ "", [ "#!/bin/bash\n", "echo ECS_CLUSTER=", { "Ref": "ECSCluster" }, " >> /etc/ecs/ecs.config" ] ] } },
        "NetworkInterfaces": [ {
          "AssociatePublicIpAddress": "true",
          "DeviceIndex": "0",
          "GroupSet": { "Ref" : "SecurityGroupId" },
          "SubnetId": { "Ref" : "SubnetId" }
        } ],
        "IamInstanceProfile": { "Ref" : "EC2InstanceProfile" },
        "Tags" : [ {"Key" : "Name", "Value" : "docker-dev01" } ]
      }
    },
    "EC2InstanceProfile": {
      "Type": "AWS::IAM::InstanceProfile",
      "Properties": {
        "Path": "/",
        "Roles": [ { "Ref": "EC2Role" } ]
      }
    }
  }
}
5. ecs.dev01.elb01.template

コンテナで利用するELBを登録します。

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "dev ecs-elb stack",
  "Parameters" : {
    "Instance" : {
      "Type" : "CommaDelimitedList",
      "Default": "【4で起動したinstance id】"
    },
    "SubnetId" : {
      "Type" : "CommaDelimitedList",
      "Default": "【サブネット】"
    },
    "SecurityGroupId" : {
      "Type" : "CommaDelimitedList",
      "Default": "【セキュリティグループ】"
    },
    "SSL" : {
      "Type" : "String",
      "Default": "arn:aws:iam::【AWS account id】:server-certificate/【登録している証明書名】"
    }
  },
  "Resources" : {
    "DevFrontendELB" : {
      "Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
      "Properties" : {
        "Instances" : { "Ref" : "Instance" },
        "LoadBalancerName" : "docker-dev-frontend-elb01",
        "ConnectionSettings" : { "IdleTimeout" : 300 },
        "Listeners" : [ { "InstancePort" : 80,
                          "InstanceProtocol" : "HTTP",
                          "LoadBalancerPort" : 443,
                          "Protocol" : "HTTPS",
                          "SSLCertificateId" : { "Ref" : "SSL" }
        } ],
        "SecurityGroups" : { "Ref" : "SecurityGroupId" },
        "Subnets" : { "Ref" : "SubnetId" }
      }
    }
  }
}
6. ecs.service.template

最後に、サービスを登録します。
ここでようやく全てが紐付いて、どのコンテナをどのEC2で起動させて、ELBで外部からそのコンテナにアクセス出来るようにします。

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "ecs task stack",
  "Parameters" : {
    "Cluster" : {
      "Type" : "String",
      "Default": "【クラスター名】"
    },
    "NginxELBName" : {
      "Type" : "String",
      "Default": "【5. elb名】"
    },
    "NginxTaskDefinition" : {
      "Type" : "String",
      "Default": "【2.Task Definitions名】:【2.で登録したバージョン】"
    },
    "NginxContainerName" : {
      "Type" : "String",
      "Default": "nginx"
    },
    "ECSRole" : {
      "Type" : "String",
      "Default": "【3.ECS Role 名】"
    }
  },
  "Resources" : {
    "Nginx": {
      "Type" : "AWS::ECS::Service",
      "Properties" : {
        "Cluster" : { "Ref" : "Cluster" },
        "DesiredCount" : 1,
        "LoadBalancers" : [ {
          "ContainerName": { "Ref" : "NginxContainerName" },
          "ContainerPort": "80",
          "LoadBalancerName" : { "Ref" : "NginxELBName" }
        } ],
        "Role" : { "Ref" : "ECSRole" },
        "TaskDefinition" : { "Ref" : "NginxTaskDefinition" }
      }
    }
    }
  }
}

これらを全て登録して、ドメインにアクセスして、コンテナ化した画面が表示されればOKです。 余談ですが、サービスの登録は1~5までのどれか1つでも失敗しているとサービスのtemplate登録には失敗して、「Did not stabilize」のエラーが出ます。
何かが安定しないんだ?これは、こちら側の問題なのか?思って結構悩んだので、
「リソースの作成に失敗しました。」とか出てくれた方が、自分のtemplateが悪いと判断ついて良いなあと思いました。
ECSの公式のsampleだとAutoScalingでインスタンスを立てていますが、この時は導入検討段階だったので、インスタンスだけで立てました。

watershedアルゴリズムを使って画像を領域毎に分割する

0. はじめに

どうも、お久しぶりです。原田@C++大好きです。

本当は、皆様にMonad実装してみた!とかやって見たかったのですが…

タイムアップでして、そのうち実装してみます。

今回は、もうScala関係なしにC++OpenCVネタです。

あーこんなのあるのかー程度に見ていただければ幸いです。

1. watershedって何さ?

watershedの意味 - 英和辞典 Weblio辞書

まずは英語、分水嶺(れい)、分水界、(川の)流域、分岐点、転機という意味です。

まぁ、何かというと画像の上から水を流しこむような形で画像の領域を判断するアルゴリズムです。

詳しくはここらあたりを見てもらえるとちょっとはわかりやすくなるかも。

ウォーターシェッド(領域分割) | 計測 | ヴィスコの画像処理技術 | ヴィスコ・テクノロジーズ株式会社

2. 実際のコード

// 面積計算アルゴリズム(画素数)
int calculateArea(cv::Mat * image) {
    cv::Mat grayMat;
    cv::cvtColor(image, grayMat, CV_BGR2GRAY);
    return cv::countNonZero(grayMat);
}

// 画像の分割アルゴリズム
void divide(cv::Mat base, cv::Mat & distA, cv::Mat & distB) {
    // Watershed分割
    // グレースケール
    cv::Mat bw;
    cv::cvtColor(base, bw, CV_BGR2GRAY);
    cv::threshold(bw, bw, 40, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
    cv::imshow("dist", bw);

    cv::Mat dist;
    // 輪郭から内側にいけばいくほど濃くする
    cv::distanceTransform(bw, dist, CV_DIST_L2, 3);
    cv::normalize(dist, dist, 0, 1., cv::NORM_MINMAX);
    cv::imshow("Distance Transform Image", dist);

    // 二値化してゴミ取り
    cv::threshold(dist, dist, .4, 1., CV_THRESH_BINARY);
    cv::Mat kernel1 = cv::Mat::ones(3, 3, CV_8UC1);
    cv::dilate(dist, dist, kernel1);
    imshow("Peaks", dist);

    cv::Mat dist_8u;
    dist.convertTo(dist_8u, CV_8U);

    // すべてのマーカーを取得
    cv::vector < cv::vector < cv::Point > > contours;
    cv::findContours(dist_8u, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    // watershedに流し込む用のマーカー画像作成
    cv::Mat markers = cv::Mat::zeros(dist.size(), CV_32SC1);
    // マーカーを描画
    for (size_t i = 0; i < contours.size(); i++) {
        cv::drawContours(markers, contours, static_cast < int > (i), cv::Scalar::all(static_cast < int > (i) + 1), -1);
    }
    // 背景用のマーカー作成
    circle(markers, cv::Point(5, 5), 3, CV_RGB(255, 255, 255), -1);
    imshow("Markers", markers * 10000);

    // ベース画像を8ビット変換
    cv::Mat stage_8bit_3channel;
    cv::cvtColor(base, stage_8bit_3channel, CV_BGR2RGB, 3);
    cv::watershed(stage_8bit_3channel, markers);
    imshow("watershed ", markers * 10000);

    cv::Mat dividA = cv::Mat::zeros(base.size(), CV_8UC4);
    cv::Mat dividB = cv::Mat::zeros(base.size(), CV_8UC4);

  // 分割した画像をそれぞれの画像に書き込む
    for (int i = 0; i < markers.rows; i++) {
        for (int j = 0; j < markers.cols; j++) {
            // 白固定してますが、色わけしたい場合は適当にいじってください。
            int r = 255;
            int g = 255;
            int b = 255;
            int index = markers.at < int > (i, j);
            if (index == 1) {
                dividA.at < cv::Vec4b > (i, j) = cv::Vec4b(r, g, b, 255);
            } else if (index == 2) {
                dividB.at < cv::Vec4b > (i, j) = cv::Vec4b(r, g, b, 255);
            } else {
                dividA.at < cv::Vec4b > (i, j) = cv::Vec4b(0, 0, 0, 0);
                dividB.at < cv::Vec4b > (i, j) = cv::Vec4b(0, 0, 0, 0);
            }
        }
    }
    imshow("dividA", dividA);
    imshow("dividB", dividB);

    distA = dividA;
    distB = dividB;

    // dividBに画像が存在したら
    if (calculateArea(distB) != 0) {
        return;
    }
  // 分水流の欠点であまりにも小さすぎると破棄されてしまうことがあるので、
  // ベース画像から他の画像を抜く処理
    for (int i = 0; i < base.rows; i++) {
        for (int j = 0; j < markers.cols; j++) {
            auto stagePixel = base.at < cv::Vec4b > (i, j);
            auto divAPixel = dividA.at < cv::Vec4b > (i, j);
            int r = 255;
            int g = 255;
            int b = 255;
            if (stagePixel[0] == 0 &&
                stagePixel[1] == 0 &&
                stagePixel[2] == 0 &&
                stagePixel[3] == 255) {
                dividB.at < cv::Vec4b > (i, j) = cv::Vec4b(0, 0, 0, 0);
                continue;
            }
            if (divAPixel[0] == r && divAPixel[1] == g && divAPixel[2] == b) {
                dividB.at < cv::Vec4b > (i, j) = cv::Vec4b(0, 0, 0, 0);
                continue;
            }
            dividB.at < cv::Vec4b > (i, j) = cv::Vec4b(r, g, b, 255);
        }
    }
    imshow("dividBv2", dividB);
    distB = dividB;
}

詳細な動作

まずは画像を二値化します。

    // グレースケール
    cv::Mat bw;
    cv::cvtColor(base, bw, CV_BGR2GRAY);
    cv::threshold(bw, bw, 40, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
    cv::imshow("dist", bw);

f:id:y_harada:20160405002916p:plain

distanceTransformで輪郭からの距離がながくなると濃くなるフィルターかけます。

    cv::Mat dist;
    // 輪郭から内側にいけばいくほど濃くする
    cv::distanceTransform(bw, dist, CV_DIST_L2, 3);
    cv::normalize(dist, dist, 0, 1., cv::NORM_MINMAX);
    cv::imshow("Distance Transform Image", dist);

f:id:y_harada:20160405002920p:plain

二値化してさっきのdistanceTransformから薄い部分を取り除き、膨張させます

    // 二値化してゴミ取り
    cv::threshold(dist, dist, .4, 1., CV_THRESH_BINARY);
    cv::Mat kernel1 = cv::Mat::ones(3, 3, CV_8UC1);
    cv::dilate(dist, dist, kernel1);
    imshow("Peaks", dist);

f:id:y_harada:20160405002922p:plain

膨張させた画像からマーカーを生成します。

この時背景もマーカーにいれておきます。

    cv::Mat dist_8u;
    dist.convertTo(dist_8u, CV_8U);

    // すべてのマーカーを取得
    cv::vector < cv::vector < cv::Point > > contours;
    cv::findContours(dist_8u, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    // watershedに流し込む用のマーカー画像作成
    cv::Mat markers = cv::Mat::zeros(dist.size(), CV_32SC1);
    // マーカーを描画
    for (size_t i = 0; i < contours.size(); i++) {
        cv::drawContours(markers, contours, static_cast < int > (i), cv::Scalar::all(static_cast < int > (i) + 1), -1);
    }
    // 背景用のマーカー作成
    circle(markers, cv::Point(5, 5), 3, CV_RGB(255, 255, 255), -1);
    imshow("Markers", markers * 10000);

f:id:y_harada:20160405002927p:plain

画像を適切なチャンネルに変更し、watershedにかけます。

    // ベース画像を8ビット変換
    cv::Mat stage_8bit_3channel;
    cv::cvtColor(base, stage_8bit_3channel, CV_BGR2RGB, 3);
    cv::watershed(stage_8bit_3channel, markers);
    imshow("watershed ", markers * 10000);

f:id:y_harada:20160405002933p:plain

最後に、各々のマーカーカラーから分離された画像を取り出します。

  // 分割した画像をそれぞれの画像に書き込む
    for (int i = 0; i < markers.rows; i++) {
        for (int j = 0; j < markers.cols; j++) {
            // 白固定してますが、色わけしたい場合は適当にいじってください。
            int r = 255;
            int g = 255;
            int b = 255;
            int index = markers.at < int > (i, j);
            if (index == 1) {
                dividA.at < cv::Vec4b > (i, j) = cv::Vec4b(r, g, b, 255);
            } else if (index == 2) {
                dividB.at < cv::Vec4b > (i, j) = cv::Vec4b(r, g, b, 255);
            } else {
                dividA.at < cv::Vec4b > (i, j) = cv::Vec4b(0, 0, 0, 0);
                dividB.at < cv::Vec4b > (i, j) = cv::Vec4b(0, 0, 0, 0);
            }
        }
    }
    imshow("dividA", dividA);
    imshow("dividB", dividB);

    distA = dividA;
    distB = dividB;

f:id:y_harada:20160405002938p:plainf:id:y_harada:20160405002935p:plain

最後に

今回はしょっぱい矩形の分割でしたが、実際の写真とか垂れ流してみると物体検知とかができます。

OpenCVにはこれの他にも、顔認識やらいろいろなフィルターが詰まっています。

画像処理を本気でやってみたい人にはいいライブラリです。(Javaでも書けるしねってことはScalaでも…)

ちなみに、ScalaOpenCVとかだと… ScalaでOpenCVを使って画像処理 « Rest Term

是非是非、触ってみてはいかがでしょうか?

戯言

最近、C++ラムダ式楽しいです。 C++ でのラムダ式

あと型推論が便利 自動 (C++

参考にさせていただいたサイト様

OpenCV 2.2 C++ リファレンス — opencv 2.2 documentation

OpenCV: Image Segmentation with Watershed Algorithm

OpenCV: Image Segmentation with Distance Transform and Watershed Algorithm

ScalaMatsuriで同人誌を配布した話《PDF有り》 / 今週末にイベント開催します

Scala お知らせ

f:id:ysugitani:20160324195656j:plain

 

こんにちは、杉谷です。 

少し前に開催されたScalaMatsuri2016に将軍スポンサーとして参加させていただいたいた際、スポンサーノベルティとしてScala同人誌の配布をさせていただきました。その際に得られた知見を共有します。

なぜ作ろうと思ったのか

ノベルティの定番といえばシール・ボールペン(最近だとフリクションが多め?)・メモ帳といったところでしょうか?

知名度のあるプロダクトや会社であればグッズとして嬉しさもあるとおもいますが、ぶっちゃけ知名度が全くない我々がそのような物を作って誰が喜ぶのか?と疑問に思ったので、もうすこし糧になりそうなものを、ということで同人誌を作ることにしました。

 

制作環境・制作フロー

制作ツールGitbookを採用しました。 MarkDownでさくさくと書けることと、ブラウザでの閲覧も想定されていて制作がしやすいことが素敵でした。

ChefとVagrantを使って、数行叩けば以下の要件を備えたVMを立てられるように整えました

  • 基本原稿ディレクトリにMarkdownファイルを書き足すだけ
  • http://sepbook/ を開くとリアルタイムプレビューが表示される
  • GitbookとNginxと、PDF出力用のフォントやライブラリが入ってる
  • GitbookはVM起動時に自動起動

あとは執筆者のマシンとCI用の機材にこれをセットアップし

  1. 執筆者の名前でブランチを切る
  2. 執筆する
  3. masterにプルリクエストをする
  4. 各種レビュー(添削・内容の検証…)
  5. マージ
  6. CIがPDFを自動作成
  7. Slackに通知 & 回覧

というフローで進めました。 

注意が必要だったこと

GitbookのPDFはなかなか上等な出力をしてくれるのですが、物理本化するにあたり以下に気をつける必要がありました。

  • コードが横に広すぎるとはみ出てしまうので、適度に改行する
  • 改ページがほどよくなるよう調整する
  • リンク( [hoge](http://〜) )は使わず、URLを注釈等で記載する

編集方針

動機の一つに"我々の等身大を知って欲しい"ということで、各チーム一人執筆者を募り、それぞれのチームで実際に採用しているScalaネタで制作していくことにしました。 できあがった記事は以下の通りです。

  • PYXIS事業でのデータパーティション設計事例
  • IntelliJのショートカット+便利機能集
  • Incoming WebHooksを使ってScalaでSlackに投稿してみる
  • Beanstalk for Dockerを使ったPlay Framework実行環境構築
  • Python学生からプロScalaエンジニアに
  • GANMA!でのCache実装例
  • Play Framework入門者向けプロジェクト分割
  • ヘキサゴナルアーキテクチャを採用したプロジェクトの実装例
  • Akka Schedulerの定期実行を使って自動的にInstagramから画像を取得し、Tumblrへ投稿
  • scala DDD 依存性逆転の原則を採用してみた
  • 今すぐ使える!Android開発に役立つKotlinの拡張関数10選

なぜかKotlinネタも混ざるくらい自由な品揃えとなりました。

レビューでは"てにおは"や誤字脱字、コードの正しさを確認するほか、"具体的な知識・知見が得られる記事であること"を重視しました。 

合計ページ数は93ページでした(+表紙・裏表紙)。

お値段

キンコーズで700部刷りまして

一冊当たりのお値段は 590円/冊 でした。(ワーオ

流石に毎年やるにはグッとくるお値段ですが、「みたよ!よかったよ!!!」的なコメントをどしどし戴けると次回も頑張れるかもしれません!

評判

直接伺えた範囲では好評でした!(なぜかKotolinがよく刺さってたような)

ネット上ではそれほどご意見を見かけなかったので、アンケートフォームへのQRコードを張っておくとよかったかもしれません。

ダウンロード 

配布した物とほぼ同等なものがこちらからダウンロード戴けます。

f:id:ysugitani:20160324200058j:plainDownload - セプテーニ・オリジナル 技術読本

 

Scala将軍達の後の祭り

今週末の 3/26(土)に将軍スポンサー4社合同(サイバーエージェント・マーベリック・チャットワーク・セプテーニオリジナル)でイベントを開催します!

scala-syogun-matsuri.connpass.com

まだまだお席はありますので、よろしければ是非ご参加ください!

 

ElasticsearchとKibanaを使ってTwitterのトレンドワードを可視化してみた

大久保です。

最近、会社でElasticsearch+Kibana+Fluentdという定番の組み合わせを使ってログ解析する機会があったので、ついでにいろいろ勉強してみました。

触ってみておもしろかったのが、Elasticsearchがログ解析だけじゃなくてちょっとしたKVSのようにも振る舞えることです。

ElasticsearchはKibanaと組み合わせることで、もっといろいろおもしろいことできそう感あります!!

本記事では、その一例としてTwitterのトレンドワードをリアルタイムに集計するプログラムを組んでみました。

Kobito.ZqzBFh.png 完成形:トレンドワードごとのツイート数をグラフ化

続きを読む