├── .deliver └── config ├── .editorconfig ├── .gitignore ├── README.md ├── build ├── config ├── config.exs ├── dev.exs └── prod.exs ├── lib ├── opencv_thumbnail_server.ex └── opencv_thumbnail_server │ ├── api.ex │ ├── exit_callback.ex │ ├── supervisor.ex │ └── worker.ex ├── mix.exs ├── mix.lock ├── priv └── main.py └── test ├── opencv_thumbnail_server_test.exs └── test_helper.exs /.deliver/config: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | APP="opencv_thumbnail_server" # name of your release 4 | 5 | BUILD_HOST="192.168.212.210" # host where to build the release 6 | BUILD_USER="ycc" # local user at build host 7 | BUILD_AT="/tmp/erlang/builds" # build directory on build host 8 | 9 | STAGING_HOSTS="192.168.212.210" # staging / test hosts separated by space 10 | STAGING_USER="ycc" # local user at staging hosts 11 | TEST_AT="/srv/telegram/deploy" # deploy directory on staging hosts. default is DELIVER_TO 12 | 13 | PRODUCTION_HOSTS="144.0.3.52" # deploy / production hosts separated by space 14 | PRODUCTION_USER="root" # local user at deploy hosts 15 | DELIVER_TO="/srv/telegram/deploy" # deploy directory on production hosts 16 | 17 | 18 | #LINK_SYS_CONFIG=/srv/opencv_thumbnail_server/rel/sys.config 19 | LINK_VM_ARGS=/srv/opencv_thumbnail_server/rel/vm.args 20 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This .editorconfig file located in project root 2 | root = true 3 | 4 | # All file types default settings 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.py] 14 | indent_size = 4 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /cover 3 | /deps 4 | erl_crash.dump 5 | *.ez 6 | /rel 7 | *.py[cod] 8 | scp 9 | /.deliver/releases 10 | .DS_Store 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to 2 | 3 | Get image width and height 4 | 5 | ``` 6 | iex> {:ok, data} = OpencvThumbnailServer.Api.load_image_url("http://ww3.sinaimg.cn/mw690/6941baebgw1epzcuv9vmxj20me0hy0u1.jpg") 7 | {:ok, <<255, 216, 255, 224, ...>>} 8 | iex> OpencvThumbnailServer.Api.get_dimension(data) 9 | {:ok, {256, 256}} 10 | ``` 11 | 12 | ## Installation 13 | 14 | The project is in early development stage, there's no package available in hex. you must install the deps with git repo. 15 | 16 | 1. Add opencv_thumbnail_server to your list of dependencies in `mix.exs`: 17 | ``` 18 | def deps do 19 | [{:opencv_thumbnail_server, github: "developerworks/opencv_thumbnail_server"}] 20 | end 21 | ``` 22 | 2. Ensure opencv_thumbnail_server is started before your application: 23 | 24 | ``` 25 | def application do 26 | [applications: [:opencv_thumbnail_server]] 27 | end 28 | ```` 29 | 30 | ## OpenCV Installation on Ubuntu 31 | http://www.pyimagesearch.com/2015/06/22/install-opencv-3-0-and-python-2-7-on-ubuntu/ 32 | 33 | ## OpenCV Installation on Mac OSX 34 | http://www.mobileway.net/2015/02/14/install-opencv-for-python-on-mac-os-x/ 35 | -------------------------------------------------------------------------------- /build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | MIX_ENV=prod mix compile 4 | MIX_ENV=prod mix release 5 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | 2 | use Mix.Config 3 | 4 | config :logger, :console, 5 | level: :debug, 6 | format: "$date $time $metadata[$level] $message\n", 7 | handle_sasl_reports: true, 8 | handle_otp_reports: true 9 | 10 | config :opencv_thumbnail_server, settings: [ 11 | # Shoud not be the system python in OSX 12 | {:python, '/usr/local/bin/python'}, 13 | # Python module use to make thumbnail 14 | {:module_name, :main}, 15 | # Poolboy configurations 16 | {:poolboy, [ 17 | name: {:local, :opencv_thumbnail_server_pool}, 18 | worker_module: OpencvThumbnailServer.Worker, 19 | size: 10, 20 | max_overflow: 20 21 | ]} 22 | ] 23 | 24 | import_config "#{Mix.env}.exs" 25 | -------------------------------------------------------------------------------- /config/dev.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :kernel, distributed: [{:opencv_thumbnail_server, 5000, [ 4 | :"node1@192.168.212.45", 5 | :"node2@192.168.212.45", 6 | :"node3@192.168.212.45", 7 | :"node4@192.168.212.45", 8 | :"node5@192.168.212.45" 9 | ]}] 10 | 11 | config :kernel, inet_dist_listen_min: 9100 12 | 13 | config :kernel, inet_dist_listen_max: 9105 14 | 15 | config :kernel, sync_nodes_mandatory: [ 16 | :"node2@192.168.212.45", 17 | :"node3@192.168.212.45" 18 | ] 19 | config :kernel, sync_nodes_optional: [ 20 | :"node4@192.168.212.45", 21 | :"node5@192.168.212.45" 22 | ] 23 | 24 | config :kernel, sync_nodes_timeout: 10000 25 | 26 | config :kernel, global_groups: [{:opencv_thumbnail_server_group, [ 27 | :"node1@192.168.212.45", 28 | :"node2@192.168.212.45", 29 | :"node3@192.168.212.45" 30 | ]}] 31 | 32 | config :syn, registry_process_exit_callback: [ 33 | OpencvThumbnailServer.ExitCallback, :callback_on_process_exit 34 | ] 35 | -------------------------------------------------------------------------------- /config/prod.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | # config :kernel, distributed: [{:opencv_thumbnail_server, 5000, [ 4 | # :"node1@192.168.212.45", 5 | # :"node2@192.168.212.45", 6 | # :"node3@192.168.212.45", 7 | # :"node4@192.168.212.45", 8 | # :"node5@192.168.212.45" 9 | # ]}] 10 | 11 | # config :kernel, inet_dist_listen_min: 9100 12 | 13 | # config :kernel, inet_dist_listen_max: 9105 14 | 15 | # config :kernel, sync_nodes_mandatory: [ 16 | # :"node2@192.168.212.45", 17 | # :"node3@192.168.212.45" 18 | # ] 19 | # config :kernel, sync_nodes_optional: [ 20 | # :"node4@192.168.212.45", 21 | # :"node5@192.168.212.45" 22 | # ] 23 | 24 | # config :kernel, sync_nodes_timeout: 10000 25 | 26 | # config :kernel, global_groups: [{:opencv_thumbnail_server_group, [ 27 | # :"node1@192.168.212.45", 28 | # :"node2@192.168.212.45", 29 | # :"node3@192.168.212.45" 30 | # ]}] 31 | 32 | # config :syn, registry_process_exit_callback: [ 33 | # OpencvThumbnailServer.ExitCallback, :callback_on_process_exit 34 | # ] 35 | -------------------------------------------------------------------------------- /lib/opencv_thumbnail_server.ex: -------------------------------------------------------------------------------- 1 | require Logger 2 | defmodule OpencvThumbnailServer do 3 | use Application 4 | def start(_type, _args) do 5 | Logger.info "Start opencv thumbnail server" 6 | # :erlang.set_cookie(node, :"&!CQMYY$_123+_-*%#ELIXIR.APPLICATION") 7 | OpencvThumbnailServer.Supervisor.start_link() 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/opencv_thumbnail_server/api.ex: -------------------------------------------------------------------------------- 1 | defmodule OpencvThumbnailServer.Api do 2 | alias OpencvThumbnailServer.Worker 3 | 4 | def get_dimension(data) do 5 | worker = :poolboy.checkout(:opencv_thumbnail_server_pool) 6 | {w, h} = Worker.call_python(worker, :get_dimension, [data]) 7 | :poolboy.checkin(:opencv_thumbnail_server_pool, worker) 8 | {w, h} 9 | end 10 | 11 | def load_image_url(url) do 12 | worker = :poolboy.checkout(:opencv_thumbnail_server_pool) 13 | image_bin = Worker.call_python(worker, :load_image_url, [url]) 14 | :poolboy.checkin(:opencv_thumbnail_server_pool, worker) 15 | image_bin 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/opencv_thumbnail_server/exit_callback.ex: -------------------------------------------------------------------------------- 1 | require Logger 2 | 3 | defmodule OpencvThumbnailServer.ExitCallback do 4 | def callback_on_process_exit(key, pid, meta, reason) do 5 | Logger.debug "Process with Key #{inspect key}, Pid #{inspect pid} and Meta #{inspect meta} exited with reason #{inspect reason}" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/opencv_thumbnail_server/supervisor.ex: -------------------------------------------------------------------------------- 1 | defmodule OpencvThumbnailServer.Supervisor do 2 | use Supervisor 3 | 4 | @config Application.get_env :opencv_thumbnail_server, :settings 5 | 6 | def start_link() do 7 | Supervisor.start_link(__MODULE__, [], name: __MODULE__) 8 | end 9 | 10 | def init([]) do 11 | pool_options = @config[:poolboy] 12 | {_, name} = pool_options[:name] 13 | children = [ 14 | :poolboy.child_spec(name, pool_options, @config[:module_name]) 15 | ] 16 | supervise(children, strategy: :one_for_all, max_restarts: 1000, max_seconds: 3600) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/opencv_thumbnail_server/worker.ex: -------------------------------------------------------------------------------- 1 | require Logger 2 | defmodule OpencvThumbnailServer.Worker do 3 | use GenServer 4 | @config Application.get_env(:opencv_thumbnail_server, :settings) 5 | 6 | 7 | def start_link(python_module) do 8 | GenServer.start_link(__MODULE__, python_module, []) 9 | end 10 | 11 | def call_python(worker, function, args) do 12 | GenServer.call(worker, {:call_python, function, args}, 10_000) 13 | end 14 | 15 | def init(python_module) do 16 | python_path = Application.app_dir(:opencv_thumbnail_server, "/priv") |> to_char_list 17 | Logger.debug "python path: #{python_path}" 18 | {:ok, pid} = :python.start_link([ 19 | {:python_path, python_path}, 20 | {:python, @config[:python]} 21 | ]) 22 | state = {python_module, pid} 23 | {:ok, state} 24 | end 25 | 26 | def handle_call({:call_python, function, args}, _from, state) do 27 | {module, pid} = state 28 | result = :python.call(pid, module, function, args) 29 | reply = {:ok, result} 30 | {:reply, reply, state} 31 | end 32 | 33 | def handle_call(_request, _from, state) do 34 | {:stop, :error, :bad_call, state} 35 | end 36 | 37 | def handle_info(_msg, {module,py_pid}) do 38 | {:stop, :error, {module,py_pid}} 39 | end 40 | 41 | def terminate(_reason, {_, py_pid}) do 42 | :python.stop(py_pid) 43 | :ok 44 | end 45 | 46 | end 47 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule OpencvThumbnailServer.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [app: :opencv_thumbnail_server, 6 | version: "0.0.5", 7 | elixir: "~> 1.2", 8 | build_embedded: Mix.env == :prod, 9 | start_permanent: Mix.env == :prod, 10 | deps: deps] 11 | end 12 | 13 | # Configuration for the OTP application 14 | # 15 | # Type "mix help compile.app" for more information 16 | def application do 17 | dev_packages = Mix.env == :dev && [:exsync, :apex] || [] 18 | [ 19 | applications: [ 20 | :logger, 21 | :poolboy, 22 | :erlport, 23 | :edeliver, 24 | :syn, 25 | :runtime_tools 26 | ] ++ dev_packages, 27 | mod: {OpencvThumbnailServer, []} 28 | ] 29 | end 30 | 31 | # Dependencies can be Hex packages: 32 | # 33 | # {:mydep, "~> 0.3.0"} 34 | # 35 | # Or git/path repositories: 36 | # 37 | # {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"} 38 | # 39 | # Type "mix help deps" for more examples and options 40 | defp deps do 41 | [ 42 | {:apex, "~> 0.5.2", only: [:dev]}, 43 | {:edeliver, ">= 1.2.10"}, 44 | {:erlport, github: "hdima/erlport"}, 45 | {:exrm, "~> 1.0.5"}, 46 | {:exfswatch, "~> 0.2.1", override: true}, 47 | {:exsync, "~> 0.1.2", only: [:dev]}, 48 | {:poolboy, "~> 1.5"}, 49 | {:syn, "~> 1.4"} 50 | ] 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{"apex": {:hex, :apex, "0.4.0", "85048fd201a06d3427d5c607d22f080a52473d737343afd0c427f9bb64239af5", [:mix], []}, 2 | "bbmustache": {:hex, :bbmustache, "1.0.4", "7ba94f971c5afd7b6617918a4bb74705e36cab36eb84b19b6a1b7ee06427aa38", [:rebar], []}, 3 | "cf": {:hex, :cf, "0.2.1", "69d0b1349fd4d7d4dc55b7f407d29d7a840bf9a1ef5af529f1ebe0ce153fc2ab", [:rebar3], []}, 4 | "edeliver": {:hex, :edeliver, "1.2.10", "89e24d7373b7f88a505dbf7c72d2dcda2c398b4796bd6b985de09d0f9f1d4565", [:mix], [{:exrm, ">= 0.16.0", [hex: :exrm, optional: false]}]}, 5 | "erlport": {:git, "https://github.com/hdima/erlport.git", "246b7722d62b87b48be66d9a871509a537728962", []}, 6 | "erlware_commons": {:hex, :erlware_commons, "0.21.0", "a04433071ad7d112edefc75ac77719dd3e6753e697ac09428fc83d7564b80b15", [:rebar3], [{:cf, "0.2.1", [hex: :cf, optional: false]}]}, 7 | "exfswatch": {:hex, :exfswatch, "0.1.1", "7ccf6fc9b443d04dada3e50b21f910b4d0e4546ce7772dc6d4181fb929ea186f", [:mix], [{:fs, "~> 0.9", [hex: :fs, optional: false]}]}, 8 | "exrm": {:hex, :exrm, "1.0.6", "f708fc091dcacb93c1da58254a1ab34166d5ac3dca162877e878fe5d7a9e9dce", [:mix], [{:relx, "~> 3.5", [hex: :relx, optional: false]}]}, 9 | "exsync": {:hex, :exsync, "0.1.2", "8913852c49729ad186a7dd2d6ada51978e34ac212bf77155e2643742f19bf2f9", [:mix], [{:exfswatch, "~> 0.1", [hex: :exfswatch, optional: false]}]}, 10 | "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], []}, 11 | "getopt": {:hex, :getopt, "0.8.2", "b17556db683000ba50370b16c0619df1337e7af7ecbf7d64fbf8d1d6bce3109b", [:rebar], []}, 12 | "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], []}, 13 | "providers": {:hex, :providers, "1.6.0", "db0e2f9043ae60c0155205fcd238d68516331d0e5146155e33d1e79dc452964a", [:rebar3], [{:getopt, "0.8.2", [hex: :getopt, optional: false]}]}, 14 | "relx": {:hex, :relx, "3.20.0", "b515b8317d25b3a1508699294c3d1fa6dc0527851dffc87446661bce21a36710", [:rebar3], [{:providers, "1.6.0", [hex: :providers, optional: false]}, {:getopt, "0.8.2", [hex: :getopt, optional: false]}, {:erlware_commons, "0.21.0", [hex: :erlware_commons, optional: false]}, {:cf, "0.2.1", [hex: :cf, optional: false]}, {:bbmustache, "1.0.4", [hex: :bbmustache, optional: false]}]}, 15 | "syn": {:hex, :syn, "1.4.0", "4de3eb031f3f986326a8fcc872162d73570f443304f7fd13caf36e36e28a118b", [:rebar3], []}} 16 | -------------------------------------------------------------------------------- /priv/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import urllib2 as urllib 4 | import numpy as np 5 | import cv2 6 | 7 | 8 | def load_image_url(url): 9 | resp = urllib.urlopen(url) 10 | buf = resp.read() 11 | return buf 12 | 13 | 14 | def load_image_file(filename): 15 | image = cv2.imdecode(filename, cv2.IMREAD_COLOR) 16 | return image 17 | 18 | def get_photo_sizes(): 19 | return [ 20 | [160, 160], 21 | [320, 320], 22 | [640, 640], 23 | [1060, 1060], 24 | [1280, 1280] 25 | ] 26 | def show(buf): 27 | # print buf 28 | # x = cv2.imdecode(image, cv2.IMREAD_COLOR) 29 | # d = cv2.cvtColor(c, cv2.COLOR_RGB2BGR) 30 | np_ndarray = np.fromstring(buf, dtype=np.uint8) 31 | x = cv2.imdecode(np_ndarray, cv2.IMREAD_UNCHANGED) 32 | return cv2.imshow('NBA Image', x) 33 | 34 | def write(buf): 35 | nparray = np.fromstring(buf, dtype=np.uint8) 36 | img = cv2.imdecode(nparray, cv2.IMREAD_UNCHANGED) 37 | return cv2.imwrite('/tmp/imwrite.png', img) 38 | 39 | # def get_dimension(): 40 | # url = 'http://img1.gtimg.com/16/1601/160106/16010642_1200x1000_0.jpg' 41 | # resp = urllib.urlopen(url) 42 | # buf = resp.read() 43 | # x = np.fromstring(buf, dtype=np.uint8) 44 | # img = cv2.imdecode(x, cv2.IMREAD_UNCHANGED) 45 | # # height = np.size(img, 0) 46 | # # width = np.size(img, 1) 47 | # height, width = image.shape[:2] 48 | # return (width, height) 49 | 50 | def get_dimension(buffer): 51 | # 把原始的二进制图片数据转换为NpArray 52 | nparray = np.fromstring(buffer, dtype=np.uint8) 53 | # 把 nparray 转换为 opencv 的图像格式 54 | image = cv2.imdecode(nparray, cv2.IMREAD_UNCHANGED) 55 | height, width = image.shape[:2] 56 | return (width, height) 57 | 58 | def convert_color(): 59 | url = 'http://ww3.sinaimg.cn/mw690/6941baebgw1epzcuv9vmxj20me0hy0u1.jpg' 60 | resp = urllib.urlopen(url) 61 | buf = resp.read() 62 | x = np.fromstring(buf, dtype=np.uint8) 63 | img = cv2.imdecode(x, cv2.IMREAD_UNCHANGED) 64 | 65 | if __name__ == '__main__': 66 | get_dimension() 67 | -------------------------------------------------------------------------------- /test/opencv_thumbnail_server_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OpencvThumbnailServerTest do 2 | use ExUnit.Case 3 | doctest OpencvThumbnailServer 4 | 5 | test "the truth" do 6 | assert 1 + 1 == 2 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | --------------------------------------------------------------------------------