class Levdon::LevdonImpl

Public Class Methods

new() click to toggle source

def LevdonImpl.dealloc(workers)

proc {|id|
  begin
    workers.each(&:stop)
    workers = nil
  rescue => e
    # nothing to do
  end
}

end

# File lib/levdon.rb, line 553
def initialize()
  @access_token = "DUMMY"
  @blocks       = {}
  @timeout      = 10.0 # default 10sec
  @request_cluster_num = 1
  
  # prediction parameters
  @ontologies = [:CLASS_ADULT]
end

Public Instance Methods

_predict_impl(parameter) click to toggle source
# File lib/levdon.rb, line 563
def _predict_impl(parameter)
  ret   = nil
  error = nil
  
  f             = parameter[:target]
  request_id    = parameter[:request_id]
  ontologies    = parameter[:ontologies]
  access_token  = parameter[:access_token]
  url           = URI.parse(API_URL)
  
  options       = {}
  options['ontologies'] = []
  error = "Class is not specified." if ontologies.length == 0
  ontologies.each{|key|
    obj = ONTOLOGY_LIST[key]
    unless obj
      error = "Invalid key => " + key.to_s 
      break
    end
    unless obj[:available]
      error = key.to_s + " class is not available.\n Reason => #{obj[:desc]}" 
      break
    end
    options['ontologies'] += [obj[:key]]
  }
  if(!error)
    stream = load(f)
    if(stream)
      io = nil
      begin
        io = StringIO.new(Levdon.prob_resize(stream))
        req = Net::HTTP::Post::Multipart.new url.path,
          "api_version"   => API_VERSION,
          "upload"        => UploadIO.new(io, "image/jpeg", "image.jpg"),
          "request_id"    => request_id,
          "access_token"  => access_token,
          "constant_id"   => APPLICATION_ID,
          "options"       => JSON.generate(options)
  
        n = Net::HTTP.new(url.host, url.port) 
        n.use_ssl = ENABLE_SSL
  
        res = n.start {|http| http.request(req) }
      
        j = JSON.parse(res.body)
        if(res.code == '200' and j['results'])
          # TODO: data struct problem
          ret = {}
          j['results'].each{|k,v|
            ret[ONTOLOGY_REV_LIST[k]] = v
          }
        else
          ret = nil
          error = j['desc']
        end
      rescue => e
        error = ""
        error += e.class.to_s + "\n"
        error += e.message.to_s + "\n"
        error += e.backtrace.to_s + "\n"
      ensure
        if(io)
          begin
            io.close()
          rescue IOError
            # nothing to do
          rescue => e
            error = ""
            error += e.class.to_s + "\n"
            error += e.message.to_s + "\n"
            error += e.backtrace.to_s + "\n"
          end
        end
      end
    else
      error = "Could not read a '#{f.to_s}'."
    end
  end
  return {:error=>error,:result=>ret}
end
async_predict(parameter,&block) click to toggle source
# File lib/levdon.rb, line 822
def async_predict(parameter,&block)
  raise "expected key: request_id" if(parameter[:request_id])
  raise "expected key: ontologies" if(parameter[:ontologies])
  raise "required key: target. Specify a file path or URL or data." unless(parameter[:target])
  parameter[:request_id] = SecureRandom.uuid.gsub("-","")
  parameter[:ontologies] = @ontologies
  enqueue(parameter)
  @blocks[VUUID+parameter[:request_id]] = block
end
async_start(access_token,&block) click to toggle source
# File lib/levdon.rb, line 644
def async_start(access_token,&block)
  @access_token = access_token
  @workers = @request_cluster_num.times.map{
    Worker.new{|*args|
      ret   = nil
      error = nil
      begin
        parameter   = args[0]
        parameter[:access_token] = access_token
        obj = _predict_impl(parameter)
        ret = obj[:result]
        error = obj[:error]
      rescue => e
        error = ""
        error += e.class.to_s + "\n"
        error += e.message.to_s + "\n"
        error += e.backtrace.to_s + "\n"
      end
      
      {:results => ret, :error => error}
    }
  }
  
  @workers.map(&:run).each(&:join)
  @roundrobin_counter       = 0
  @task_stacking_counter    = []
  @workers.length.times{|i| @task_stacking_counter.push([i,0]) }
  #ObjectSpace.define_finalizer(self,LevdonImpl.dealloc(@workers))
  
  # CTRL + C
  Signal.trap(:INT) {
    puts "Stop " + APPLICATION_ID + " clients "
    close()
    exit(0)
  }
  
  block.call({:error => nil})
end
close() click to toggle source
# File lib/levdon.rb, line 835
def close
  begin
    @workers.each(&:stop)
  rescue => e
    # nothing to do
  end
end
detect_content_type(ouri) click to toggle source
# File lib/levdon.rb, line 700
def detect_content_type(ouri)
  if(ouri)
    if(ouri.respond_to?(:content_type))
      ct = ouri.content_type
      if(ct)
        if(ct.index('image'))
          return 'image'
        elsif(ct.index('video'))
          return 'video'
        end
      end
    else
      return 'file'
    end
  end
  return nil
end
detect_image_or_video_from_path(path) click to toggle source
# File lib/levdon.rb, line 718
def detect_image_or_video_from_path(path)
  ext = File.extname(path).downcase
  if(ext == ".jpeg" || ext == ".png" || ext == ".gif" || ext == ".jpg" || ext == ".tiff" || ext == ".tif" || ext == ".psd" || ext == ".pdf")
    return "image"
  elsif(ext == ".mp4" || ext == ".avi" || ext == ".mov" || ext == ".3gp" || ext == ".flv" || ext == ".wmv")
    return "video"
  end
  return nil
end
enqueue(*args) click to toggle source
# File lib/levdon.rb, line 812
def enqueue(*args)
  target = @task_stacking_counter.sort{|a,b| a[1]-b[1]}[0][0]
  @task_stacking_counter[target][1] += 1
  #target = @roundrobin_counter % @workers.length
  #@roundrobin_counter += 1
  
  worker = @workers[target]
  worker.async_execute(*args)
end
generate_thumb_from_video(f) click to toggle source
# File lib/levdon.rb, line 728
def generate_thumb_from_video(f)
  thumb_url = "tmp"+Process.pid.to_s
  movie = FFMPEG::Movie.new(f)
  duration = (movie.duration / 2).floor
  path = "#{thumb_url}.jpg"
  movie.screenshot(path,  seek_time: duration)
  data = File.open(path).read()
  File.delete(path)
  return data
end
load(any) click to toggle source
# File lib/levdon.rb, line 749
def load(any)
  if(any.class.name == "String")
    if(any.index("http://") == 0 or any.index("https://") == 0)
      # from network
      ouri = open(any)
      ct = detect_content_type(ouri)
      new_data = nil
      if(ct == "video")
        new_data = generate_thumb_from_video(ouri.path)
      elsif(ct == "image")
        new_data = ouri.read
      end
      return new_data
    else
      if File::ftype(any) == "file"
        ct = detect_image_or_video_from_path(any)
        if(ct == "video")
          return generate_thumb_from_video(any)
        elsif(ct ==  "image")
          return File.open(any).read
        end
        puts "Invalid media file."
        return nil
      elsif(File::ftype(any) == "directory")
        puts "Can't load a directory. Target should be a file or URL."
        return nil
      else
        # from memory
        return any
      end
    end
  else
    # from memory
    return any
  end
  return nil
end
option(obj) click to toggle source
# File lib/levdon.rb, line 688
def option(obj)
  obj.each{|k,v|
    if(k == :CLASS)
      @ontologies = v
    elsif(k == :PARALLEL)
      @request_cluster_num = v
    else
      puts "Invalid option : " + k.to_s
    end
  }
end
poll() click to toggle source
# File lib/levdon.rb, line 787
def poll
  @workers.each(&:poll)
  @workers.map(&:nonblock_read_from_child).each_with_index{|ret,i|
    begin
      if(ret)
        request_id = ret[:parameter][:request_id]
        key = VUUID + request_id
        if(@blocks.has_key?(key))
          @task_stacking_counter[i][1] -= 1
          @blocks[key].call(ret)
          @blocks.delete(key)
        else
          puts "Error"
          puts "Invalid response from server."
        end
      end
    rescue => e
      puts "Response error"
      puts e.class
      puts e.message
      puts e.backtrace
    end
  }
end
predict(parameter) click to toggle source
# File lib/levdon.rb, line 843
def predict(parameter)
  if(parameter.class.name == "String")
    parameter = {:target => parameter }
  elsif(parameter.class.name == "Hash")
    raise "expected key: request_id" if(parameter[:request_id])
    raise "expected key: ontologies" if(parameter[:ontologies])
    raise "required key: target. Specify a file path or URL or data." unless(parameter[:target])
  else
    raise "Invalid parameter type"
  end
  parameter[:request_id]    = SecureRandom.uuid.gsub("-","")
  parameter[:ontologies]    = @ontologies
  parameter[:access_token]  = @access_token
  
  ret   = nil
  error = nil
  begin
    obj         = _predict_impl(parameter)
    ret         = obj[:result]
    error       = obj[:error]
  rescue => e
    error = ""
    error += e.class.to_s + "\n"
    error += e.message.to_s + "\n"
    error += e.backtrace.to_s + "\n"
  end
  return {:results => ret, :error => error}
end
queue_size() click to toggle source
# File lib/levdon.rb, line 831
def queue_size
  @blocks.length
end
start(access_token) click to toggle source
# File lib/levdon.rb, line 683
def start(access_token)
  @access_token = access_token
  return {:erorr => nil}
end
to_image(fname,ct) click to toggle source
# File lib/levdon.rb, line 739
def to_image(fname,ct)
  if(ct == "video")
    return generate_thumb_from_video(fname)
  elsif(ct == "image")
    return File.open(fname).read
  end
  puts "Invalid media stream."
  return nil
end