diff --git a/SConstruct b/SConstruct index d7796e42..1652779e 100644 --- a/SConstruct +++ b/SConstruct @@ -35,27 +35,28 @@ def buildServer(env): 'src/QueryHandler.cc', 'src/QueryMessage.cc', 'src/CommunicationManager.cc', + 'src/ExceptionsCommand.cc', 'src/PMGDQuery.cc', 'src/SearchExpression.cc', 'src/PMGDIterators.cc', 'src/PMGDQueryHandler.cc', 'src/RSCommand.cc', 'src/ImageCommand.cc', - 'src/ExceptionsCommand.cc', 'src/DescriptorsManager.cc', 'src/DescriptorsCommand.cc', 'src/BoundingBoxCommand.cc', + 'src/VideoCommand.cc', ] - vdms = env.Program('vdms', vdms_server_files) + env.Program('vdms', vdms_server_files) # Set INTEL_PATH. First check arguments, then enviroment, then default if ARGUMENTS.get('INTEL_PATH', '') != '': - intel_path = ARGUMENTS.get("INTEL_PATH", '') + intel_path = ARGUMENTS.get("INTEL_PATH", '') elif os.environ.get('INTEL_PATH', '') != '': - intel_path = os.environ.get('INTEL_PATH', '') + intel_path = os.environ.get('INTEL_PATH', '') else: - intel_path = os.getcwd() + intel_path = os.getcwd() # Enviroment use by all the builds env = Environment(CXXFLAGS="-std=c++11 -O3") @@ -66,6 +67,6 @@ SConscript(os.path.join('utils', 'SConscript'), exports=['env']) SConscript(os.path.join('client/cpp','SConscript'), exports=['env']) if GetOption('no-server'): - buildServer(env) - # Build tests only if server is built - SConscript(os.path.join('tests', 'SConscript'), exports=['env']) + buildServer(env) + # Build tests only if server is built + SConscript(os.path.join('tests', 'SConscript'), exports=['env']) diff --git a/src/BoundingBoxCommand.cc b/src/BoundingBoxCommand.cc index 39670932..44dbf201 100644 --- a/src/BoundingBoxCommand.cc +++ b/src/BoundingBoxCommand.cc @@ -316,19 +316,19 @@ Json::Value FindBoundingBox::construct_responses( get_value(coords, "w"), get_value(coords, "h"))); - VCL::ImageFormat format = - img.get_image_format() != VCL::TDB ? - img.get_image_format() : VCL::PNG; + VCL::Image::Format format = + img.get_image_format() != VCL::Image::Format::TDB ? + img.get_image_format() : VCL::Image::Format::PNG; if (cmd.isMember("format")) { std::string requested_format = get_value(cmd, "format"); if (requested_format == "png") { - format = VCL::PNG; + format = VCL::Image::Format::PNG; } else if(requested_format == "jpg") { - format = VCL::JPG; + format = VCL::Image::Format::JPG; } } diff --git a/src/ImageCommand.cc b/src/ImageCommand.cc index c2981e67..bda9229e 100644 --- a/src/ImageCommand.cc +++ b/src/ImageCommand.cc @@ -103,21 +103,21 @@ int AddImage::construct_protobuf(PMGDQuery& query, } std::string img_root = _storage_tdb; - VCL::ImageFormat vcl_format = VCL::TDB; + VCL::Image::Format vcl_format = VCL::Image::Format::TDB; + std::string format = get_value(cmd, "format", ""); if (cmd.isMember("format")) { - std::string format = get_value(cmd, "format"); if (format == "png") { - vcl_format = VCL::PNG; + vcl_format = VCL::Image::Format::PNG; img_root = _storage_png; } else if (format == "tdb") { - vcl_format = VCL::TDB; + vcl_format = VCL::Image::Format::TDB; img_root = _storage_tdb; } else if (format == "jpg") { - vcl_format = VCL::JPG; + vcl_format = VCL::Image::Format::JPG; img_root = _storage_jpg; } else { @@ -127,7 +127,7 @@ int AddImage::construct_protobuf(PMGDQuery& query, } } - std::string file_name = img.create_unique(img_root, vcl_format); + std::string file_name = VCL::create_unique(img_root, format); // Modifiyng the existing properties that the user gives // is a good option to make the AddNode more simple. @@ -267,18 +267,18 @@ Json::Value FindImage::construct_responses( // We will return the image in the format the user // request, or on its format in disk, except for the case // of .tdb, where we will encode as png. - VCL::ImageFormat format = img.get_image_format() != VCL::TDB ? - img.get_image_format() : VCL::PNG; + VCL::Image::Format format = img.get_image_format() != VCL::Image::Format::TDB ? + img.get_image_format() : VCL::Image::Format::PNG; if (cmd.isMember("format")) { std::string requested_format = get_value(cmd, "format"); if (requested_format == "png") { - format = VCL::PNG; + format = VCL::Image::Format::PNG; } else if (requested_format == "jpg") { - format = VCL::JPG; + format = VCL::Image::Format::JPG; } else { Json::Value return_error; diff --git a/src/QueryHandler.cc b/src/QueryHandler.cc index 8f26ab6e..4f2dd8e0 100644 --- a/src/QueryHandler.cc +++ b/src/QueryHandler.cc @@ -37,6 +37,8 @@ #include "ImageCommand.h" #include "DescriptorsCommand.h" #include "BoundingBoxCommand.h" +#include "VideoCommand.h" + #include "ExceptionsCommand.h" #include "PMGDQuery.h" @@ -61,21 +63,29 @@ void QueryHandler::init() _rs_cmds["AddEntity"] = new AddEntity(); _rs_cmds["UpdateEntity"] = new UpdateEntity(); + _rs_cmds["FindEntity"] = new FindEntity(); + _rs_cmds["AddConnection"] = new AddConnection(); _rs_cmds["UpdateConnection"] = new UpdateConnection(); - _rs_cmds["FindEntity"] = new FindEntity(); _rs_cmds["FindConnection"] = new FindConnection(); + _rs_cmds["AddImage"] = new AddImage(); _rs_cmds["UpdateImage"] = new UpdateImage(); _rs_cmds["FindImage"] = new FindImage(); + _rs_cmds["AddDescriptorSet"] = new AddDescriptorSet(); _rs_cmds["AddDescriptor"] = new AddDescriptor(); - _rs_cmds["ClassifyDescriptor"] = new ClassifyDescriptor(); _rs_cmds["FindDescriptor"] = new FindDescriptor(); + _rs_cmds["ClassifyDescriptor"] = new ClassifyDescriptor(); + _rs_cmds["AddBoundingBox"] = new AddBoundingBox(); _rs_cmds["UpdateBoundingBox"] = new UpdateBoundingBox(); _rs_cmds["FindBoundingBox"] = new FindBoundingBox(); + _rs_cmds["AddVideo"] = new AddVideo(); + _rs_cmds["UpdateVideo"] = new UpdateVideo(); + _rs_cmds["FindVideo"] = new FindVideo(); + // Load the string containing the schema (api_schema/APISchema.h) Json::Reader reader; Json::Value api_schema; @@ -239,12 +249,21 @@ int QueryHandler::parse_commands(const protobufs::queryMessage& proto_query, return 0; } -void QueryHandler::cleanup_query(const std::vector& images) +// TODO create a better mechanism to cleanup queries that +// includes feature vectors and user-defined blobs +// For now, we do it for videos/images as a starting point. +void QueryHandler::cleanup_query(const std::vector& images, + const std::vector& videos) { for (auto& img_path : images) { VCL::Image img(img_path); img.delete_image(); } + + for (auto& vid_path : videos) { + VCL::Video img(vid_path); + img.delete_video(); + } } void QueryHandler::process_query(protobufs::queryMessage& proto_query, @@ -272,11 +291,12 @@ void QueryHandler::process_query(protobufs::queryMessage& proto_query, Json::Value cmd_result; Json::Value cmd_current; std::vector images_log; + std::vector videos_log; std::vector construct_results; auto error = [&](Json::Value& res, Json::Value& failed_command) { - cleanup_query(images_log); + cleanup_query(images_log, videos_log); res["FailedCommand"] = failed_command; json_responses.clear(); json_responses.append(res); @@ -313,6 +333,9 @@ void QueryHandler::process_query(protobufs::queryMessage& proto_query, if (cmd_result.isMember("image_added")) { images_log.push_back(cmd_result["image_added"].asString()); } + if (cmd_result.isMember("video_added")) { + videos_log.push_back(cmd_result["video_added"].asString()); + } if (ret_code != 0) { error(cmd_result, root[j]); diff --git a/src/QueryHandler.h b/src/QueryHandler.h index 1ec6b24b..6147407f 100644 --- a/src/QueryHandler.h +++ b/src/QueryHandler.h @@ -61,7 +61,9 @@ typedef ::google::protobuf::RepeatedPtrField BlobArray; bool syntax_checker(const Json::Value &root, Json::Value& error); int parse_commands(const protobufs::queryMessage& proto_query, Json::Value& root); - void cleanup_query(const std::vector& images); + void cleanup_query(const std::vector& images, + const std::vector& videos); + void process_query(protobufs::queryMessage& proto_query, protobufs::queryMessage& response); diff --git a/src/VDMSConfig.cc b/src/VDMSConfig.cc index 40859f9c..f3483ee0 100644 --- a/src/VDMSConfig.cc +++ b/src/VDMSConfig.cc @@ -49,6 +49,7 @@ #define DEFAULT_PATH_PNG "png" #define DEFAULT_PATH_TDB "tdb" #define DEFAULT_PATH_BLOBS "blobs" +#define DEFAULT_PATH_VIDEOS "videos" #define DEFAULT_PATH_DESCRIPTORS "descriptors" using namespace VDMS; @@ -176,6 +177,11 @@ void VDMSConfig::build_dirs() path_blobs = get_string_value(PARAM_DB_BLOBS, path_blobs); check_or_create(path_blobs); + // VIDEOS + path_videos = path_root + "/" + DEFAULT_PATH_VIDEOS; + path_videos = get_string_value(PARAM_DB_VIDEOS, path_videos); + check_or_create(path_videos); + // DESCRIPTORS path_descriptors = path_root + "/" + DEFAULT_PATH_DESCRIPTORS; path_descriptors = get_string_value(PARAM_DB_DESCRIPTORS, path_descriptors); diff --git a/src/VDMSConfig.h b/src/VDMSConfig.h index 3f4b4fbd..53e38542 100644 --- a/src/VDMSConfig.h +++ b/src/VDMSConfig.h @@ -42,6 +42,7 @@ #define PARAM_DB_JPG "jpg_path" #define PARAM_DB_TDB "tdb_path" #define PARAM_DB_BLOBS "blobs_path" +#define PARAM_DB_VIDEOS "videos_path" #define PARAM_DB_DESCRIPTORS "descriptors_path" #define PARAM_PMGD_NUM_ALLOCATORS "pmgd_num_allocators" @@ -69,6 +70,7 @@ namespace VDMS{ std::string path_jpg; std::string path_tdb; std::string path_blobs; + std::string path_videos; std::string path_descriptors; VDMSConfig(std::string config_file); @@ -80,13 +82,14 @@ namespace VDMS{ public: int get_int_value(std::string val, int def); std::string get_string_value(std::string val, std::string def); - std::string get_path_root() {return path_root;} - std::string get_path_pmgd() {return path_pmgd;} - std::string get_path_jpg() {return path_jpg;} - std::string get_path_png() {return path_png;} - std::string get_path_tdb() {return path_tdb;} - std::string get_path_blobs() {return path_blobs;} - std::string get_path_descriptors() {return path_descriptors;} + const std::string& get_path_root() {return path_root;} + const std::string& get_path_pmgd() {return path_pmgd;} + const std::string& get_path_jpg() {return path_jpg;} + const std::string& get_path_png() {return path_png;} + const std::string& get_path_tdb() {return path_tdb;} + const std::string& get_path_blobs() {return path_blobs;} + const std::string& get_path_videos(){return path_videos;} + const std::string& get_path_descriptors() {return path_descriptors;} }; }; // vdms namespace diff --git a/src/VideoCommand.cc b/src/VideoCommand.cc new file mode 100644 index 00000000..17bdc77e --- /dev/null +++ b/src/VideoCommand.cc @@ -0,0 +1,352 @@ +/** + * @file VideoCommand.cc + * + * @section LICENSE + * + * The MIT License + * + * @copyright Copyright (c) 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include +#include + +#include "VideoCommand.h" +#include "VDMSConfig.h" +#include "defines.h" + +using namespace VDMS; + +VideoCommand::VideoCommand(const std::string &cmd_name): + RSCommand(cmd_name) +{ +} + +void VideoCommand::enqueue_operations(VCL::Video& video, const Json::Value& ops) +{ + // Correct operation type and parameters are guaranteed at this point + for (auto& op : ops) { + const std::string& type = get_value(op, "type"); + std::string unit ; + if (type == "threshold") { + video.threshold(get_value(op, "value")); + + } + else if (type == "interval") { + + video.interval( + VCL::Video::FRAMES, + get_value(op, "start"), + get_value(op, "stop"), + get_value(op, "step")); + + } + else if (type == "resize") { + video.resize(get_value(op, "height"), + get_value(op, "width") ); + + } + else if (type == "crop") { + video.crop(VCL::Rectangle ( + get_value(op, "x"), + get_value(op, "y"), + get_value(op, "width"), + get_value(op, "height") )); + } + else { + throw ExceptionCommand(ImageError, "Operation not defined"); + } + } +} + +VCL::Video::Codec VideoCommand::string_to_codec(const std::string& codec) +{ + if (codec == "h263") { + return VCL::Video::Codec::H263; + } + else if (codec == "xvid") { + return VCL::Video::Codec::XVID; + } + else if (codec == "h264") { + return VCL::Video::Codec::H264; + } + + return VCL::Video::Codec::NOCODEC; +} + +//========= AddVideo definitions ========= + +AddVideo::AddVideo() : VideoCommand("AddVideo") +{ + _storage_video = VDMSConfig::instance()->get_path_videos(); +} + +int AddVideo::construct_protobuf( + PMGDQuery& query, + const Json::Value& jsoncmd, + const std::string& blob, + int grp_id, + Json::Value& error) +{ + const Json::Value& cmd = jsoncmd[_cmd_name]; + + int node_ref = get_value(cmd, "_ref", + query.get_available_reference()); + + VCL::Video video((void*)blob.data(), blob.size()); + + if (cmd.isMember("operations")) { + enqueue_operations(video, cmd["operations"]); + } + + // The container and codec are checked by the schema. + // We default to mp4 and h264, if not specified + const std::string& container = + get_value(cmd, "container", "mp4"); + const std::string& file_name = + VCL::create_unique(_storage_video, container); + + // Modifiyng the existing properties that the user gives + // is a good option to make the AddNode more simple. + // This is not ideal since we are manupulating with user's + // input, but for now it is an acceptable solution. + Json::Value props = get_value(cmd, "properties"); + props[VDMS_VID_PATH_PROP] = file_name; + + // Add Video node + query.AddNode(node_ref, VDMS_VID_TAG, props, Json::Value()); + + const std::string& codec = get_value(cmd, "codec", "h264"); + VCL::Video::Codec vcl_codec = string_to_codec(codec); + + video.store(file_name, vcl_codec); + + // In case we need to cleanup the query + error["video_added"] = file_name; + + if (cmd.isMember("link")) { + add_link(query, cmd["link"], node_ref, VDMS_VID_EDGE); + } + + return 0; +} + +//========= UpdateImage definitions ========= + +UpdateVideo::UpdateVideo() : VideoCommand("UpdateVideo") +{ +} + +int UpdateVideo::construct_protobuf( + PMGDQuery& query, + const Json::Value& jsoncmd, + const std::string& blob, + int grp_id, + Json::Value& error) +{ + const Json::Value& cmd = jsoncmd[_cmd_name]; + + int node_ref = get_value(cmd, "_ref", -1); + + Json::Value constraints = get_value(cmd, "constraints"); + + Json::Value props = get_value(cmd, "properties"); + + Json::Value remove_props = get_value(cmd, "remove_props"); + + // Update Image node + query.UpdateNode(node_ref, VDMS_VID_TAG, props, + remove_props, + constraints, + get_value(cmd, "unique", false)); + + return 0; +} + +Json::Value UpdateVideo::construct_responses( + Json::Value& responses, + const Json::Value& json, + protobufs::queryMessage &query_res, + const std::string &blob) +{ + assert(responses.size() == 1); + + Json::Value ret; + + // TODO In order to support "codec" or "operations", we could + // implement VCL save operation here. + + ret[_cmd_name].swap(responses[0]); + return ret; +} + +//========= FindVideo definitions ========= + +FindVideo::FindVideo() : VideoCommand("FindVideo") +{ +} + +int FindVideo::construct_protobuf( + PMGDQuery& query, + const Json::Value& jsoncmd, + const std::string& blob, + int grp_id, + Json::Value& error) +{ + const Json::Value& cmd = jsoncmd[_cmd_name]; + + Json::Value results = get_value(cmd, "results"); + + // Unless otherwhise specified, we return the blob. + if (get_value(results, "blob", true)){ + results["list"].append(VDMS_VID_PATH_PROP); + } + + query.QueryNode( + get_value(cmd, "_ref", -1), + VDMS_VID_TAG, + cmd["link"], + cmd["constraints"], + results, + get_value(cmd, "unique", false) + ); + + return 0; +} + +Json::Value FindVideo::construct_responses( + Json::Value& responses, + const Json::Value& json, + protobufs::queryMessage &query_res, + const std::string &blob) +{ + const Json::Value& cmd = json[_cmd_name]; + + Json::Value ret; + + auto error = [&](Json::Value& res) + { + ret[_cmd_name] = res; + return ret; + }; + + if (responses.size() != 1) { + Json::Value return_error; + return_error["status"] = RSCommand::Error; + return_error["info"] = "PMGD Response Bad Size"; + error(return_error); + } + + Json::Value& FindVideo = responses[0]; + + assert(FindVideo.isMember("entities")); + + if (FindVideo["status"] != 0) { + FindVideo["status"] = RSCommand::Error; + // Uses PMGD info error. + error(FindVideo); + } + + bool flag_empty = true; + + for (auto& ent : FindVideo["entities"]) { + + if(!ent.isMember(VDMS_VID_PATH_PROP)){ + continue; + } + + std::string video_path = ent[VDMS_VID_PATH_PROP].asString(); + ent.removeMember(VDMS_VID_PATH_PROP); + + if (ent.getMemberNames().size() > 0) { + flag_empty = false; + } + try { + if (!cmd.isMember("operations") && + !cmd.isMember("container") && + !cmd.isMember("codec")) + { + // Return video as is. + std::ifstream ifile(video_path, std::ifstream::in); + ifile.seekg(0, std::ios::end); + size_t encoded_size = (long)ifile.tellg(); + ifile.seekg(0, std::ios::beg); + + std::string* video_str = query_res.add_blobs(); + video_str->resize(encoded_size); + ifile.read((char*)(video_str->data()), encoded_size); + ifile.close(); + } + else { + + VCL::Video video(video_path); + + if (cmd.isMember("operations")) { + enqueue_operations(video, cmd["operations"]); + } + + const std::string& container = + get_value(cmd, "container", "mp4"); + const std::string& file_name = + VCL::create_unique("/tmp/", container); + const std::string& codec = + get_value(cmd, "codec", "h264"); + + VCL::Video::Codec vcl_codec = string_to_codec(codec); + video.store(file_name, vcl_codec); // to /tmp/ for encoding. + + auto video_enc = video.get_encoded(); + int size = video_enc.size(); + + if (size > 0) { + + std::string* video_str = query_res.add_blobs(); + video_str->resize(size); + std::memcpy((void*)video_str->data(), + (void*)video_enc.data(), + size); + } + else { + Json::Value return_error; + return_error["status"] = RSCommand::Error; + return_error["info"] = "Video Data not found"; + error(return_error); + } + } + } catch (VCL::Exception e) { + print_exception(e); + Json::Value return_error; + return_error["status"] = RSCommand::Error; + return_error["info"] = "VCL Exception"; + error(return_error); + } + } + + if (flag_empty) { + FindVideo.removeMember("entities"); + } + + ret[_cmd_name].swap(FindVideo); + return ret; +} diff --git a/src/VideoCommand.h b/src/VideoCommand.h new file mode 100644 index 00000000..89c37d15 --- /dev/null +++ b/src/VideoCommand.h @@ -0,0 +1,119 @@ +/** + * @file VideoCommand.h + * + * @section LICENSE + * + * The MIT License + * + * @copyright Copyright (c) 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#pragma once +#include +#include +#include +#include "VCL.h" + +#include "RSCommand.h" +#include "ExceptionsCommand.h" + +namespace VDMS { + +// Helper classes for handling various JSON commands. + + class VideoCommand: public RSCommand + { + protected: + void enqueue_operations(VCL::Video& video, const Json::Value& op); + + VCL::Video::Codec string_to_codec(const std::string& codec); + + public: + + VideoCommand(const std::string &cmd_name); + + virtual int construct_protobuf(PMGDQuery& tx, + const Json::Value& root, + const std::string& blob, + int grp_id, + Json::Value& error) = 0; + + virtual bool need_blob(const Json::Value& cmd) { return false; } + }; + + class AddVideo: public VideoCommand + { + const std::string DEFAULT_VIDEO_PATH = "videos/database"; + + std::string _storage_video; + + public: + AddVideo(); + + int construct_protobuf(PMGDQuery& tx, + const Json::Value& root, + const std::string& blob, + int grp_id, + Json::Value& error); + + bool need_blob(const Json::Value& cmd) { return true; } + }; + + class UpdateVideo: public VideoCommand + { + public: + UpdateVideo(); + + int construct_protobuf(PMGDQuery& tx, + const Json::Value& root, + const std::string& blob, + int grp_id, + Json::Value& error); + + Json::Value construct_responses( + Json::Value &json_responses, + const Json::Value &json, + protobufs::queryMessage &response, + const std::string &blob); + }; + + class FindVideo: public VideoCommand + { + public: + FindVideo(); + + int construct_protobuf(PMGDQuery& tx, + const Json::Value& root, + const std::string& blob, + int grp_id, + Json::Value& error); + + Json::Value construct_responses( + Json::Value &json_responses, + const Json::Value &json, + protobufs::queryMessage &response, + const std::string &blob); + }; + +}; // namespace VDMS diff --git a/src/defines.h b/src/defines.h index aa8dd73a..c5eb62f4 100644 --- a/src/defines.h +++ b/src/defines.h @@ -81,3 +81,9 @@ #define VDMS_ROI_COORD_Y_PROP "VD:y1" #define VDMS_ROI_WIDTH_PROP "VD:width" #define VDMS_ROI_HEIGHT_PROP "VD:height" + +// Videos + +#define VDMS_VID_TAG "VD:VID" +#define VDMS_VID_EDGE "VD:VIDLINK" +#define VDMS_VID_PATH_PROP "VD:videoPath" diff --git a/tests/SConscript b/tests/SConscript index a4b9f946..06707db0 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -14,9 +14,9 @@ testenv.Replace( ]) testenv.Append( - LIBS = ['gtest'], - LIBPATH = ['../utils/'] - ) + LIBS = ['gtest'], + LIBPATH = ['../utils/'] + ) testenv.MergeFlags(GetOption('cflags')) @@ -29,6 +29,7 @@ query_tests = testenv.Program( '../src/VDMSConfig.o', '../src/RSCommand.o', '../src/ImageCommand.o', + '../src/VideoCommand.o', '../src/ExceptionsCommand.o', '../src/PMGDIterators.o', '../src/PMGDQueryHandler.o', diff --git a/tests/python/TestImages.py b/tests/python/TestImages.py index 1b8c2d85..d777f50a 100644 --- a/tests/python/TestImages.py +++ b/tests/python/TestImages.py @@ -37,7 +37,7 @@ hostname = "localhost" port = 55557 -class TestAddImage(unittest.TestCase): +class TestImages(unittest.TestCase): #Methos to insert one image def insertImage(self, db, props=None, collections=None, format="png"): @@ -251,6 +251,7 @@ def test_addImageWithLink(self): addImage = {} addImage["properties"] = props addImage["link"] = link + addImage["format"] = "png" imgs_arr = [] @@ -265,11 +266,7 @@ def test_addImageWithLink(self): all_queries.append(query) - # print json.dumps(all_queries) - # vdms.aux_print_json(all_queries) - response, res_arr = db.query(all_queries, [imgs_arr]) - # vdms.aux_print_json(response) self.assertEqual(response[0]["AddEntity"]["status"], 0) self.assertEqual(response[1]["AddImage"]["status"], 0) diff --git a/tests/python/TestVideos.py b/tests/python/TestVideos.py new file mode 100644 index 00000000..05b62256 --- /dev/null +++ b/tests/python/TestVideos.py @@ -0,0 +1,335 @@ +# +# The MIT License +# +# @copyright Copyright (c) 2017 Intel Corporation +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +from threading import Thread +import sys +import os +import urllib +import time +import json +import unittest +import numpy as np +import vdms + +hostname = "localhost" +port = 55557 + +class TestVideos(unittest.TestCase): + + #Methos to insert one image + def insertVideo(self, db, props=None): + + video_arr = [] + all_queries = [] + + fd = open("../test_videos/Megamind.avi", 'rb') + video_arr.append(fd.read()) + fd.close() + + video_parms = {} + + # adds some prop + if not props is None: + props["test_case"] = "test_case_prop" + video_parms["properties"] = props + + video_parms["codec"] = "h264" + video_parms["container"] = "mp4" + + query = {} + query["AddVideo"] = video_parms + + all_queries.append(query) + + response, res_arr = db.query(all_queries, [video_arr]) + + self.assertEqual(len(response), 1) + self.assertEqual(response[0]["AddVideo"]["status"], 0) + + def test_addVideo(self): + db = vdms.vdms() + db.connect(hostname, port) + + all_queries = [] + video_arr = [] + + number_of_inserts = 2 + + for i in range(0,number_of_inserts): + #Read Brain Image + fd = open("../test_videos/Megamind.avi", 'rb') + video_arr.append(fd.read()) + fd.close() + + op_params_resize = {} + op_params_resize["height"] = 512 + op_params_resize["width"] = 512 + op_params_resize["type"] = "resize" + + props = {} + props["name"] = "video_" + str(i) + props["doctor"] = "Dr. Strange Love" + + video_parms = {} + video_parms["properties"] = props + video_parms["codec"] = "h264" + + query = {} + query["AddVideo"] = video_parms + + all_queries.append(query) + + response, obj_array = db.query(all_queries, [video_arr]) + self.assertEqual(len(response), number_of_inserts) + for i in range(0, number_of_inserts): + self.assertEqual(response[i]["AddVideo"]["status"], 0) + + def test_findVideo(self): + db = vdms.vdms() + db.connect(hostname, port) + + prefix_name = "video_1_" + + number_of_inserts = 2 + + for i in range(0,number_of_inserts): + props = {} + props["name"] = prefix_name + str(i) + self.insertVideo(db, props=props) + + all_queries = [] + + for i in range(0,number_of_inserts): + constraints = {} + constraints["name"] = ["==", prefix_name + str(i)] + + video_parms = {} + video_parms["constraints"] = constraints + + query = {} + query["FindVideo"] = video_parms + + all_queries.append(query) + + response, vid_array = db.query(all_queries) + + self.assertEqual(len(response), number_of_inserts) + self.assertEqual(len(vid_array), number_of_inserts) + for i in range(0, number_of_inserts): + self.assertEqual(response[i]["FindVideo"]["status"], 0) + + def test_findVideoResults(self): + db = vdms.vdms() + db.connect(hostname, port) + + prefix_name = "resvideo_1_" + + number_of_inserts = 2 + + for i in range(0,number_of_inserts): + props = {} + props["name"] = prefix_name + str(i) + self.insertVideo(db, props=props) + + all_queries = [] + + for i in range(0,number_of_inserts): + constraints = {} + constraints["name"] = ["==", prefix_name + str(i)] + + results = {} + results["list"] = ["name"] + + video_parms = {} + video_parms["constraints"] = constraints + video_parms["results"] = results + + query = {} + query["FindVideo"] = video_parms + + all_queries.append(query) + + response, vid_array = db.query(all_queries) + + self.assertEqual(len(response), number_of_inserts) + self.assertEqual(len(vid_array), number_of_inserts) + for i in range(0, number_of_inserts): + self.assertEqual(response[i]["FindVideo"]["status"], 0) + + def test_addVideoWithLink(self): + db = vdms.vdms() + db.connect(hostname, port) + + all_queries = [] + + props = {} + props["name"] = "Luis" + props["lastname"] = "Ferro" + props["age"] = 27 + + addEntity = {} + addEntity["_ref"] = 32 + addEntity["properties"] = props + addEntity["class"] = "AwPeopleVid" + + query = {} + query["AddEntity"] = addEntity + + all_queries.append(query) + + props = {} + props["name"] = "Luis" + props["lastname"] = "Malo" + props["age"] = 27 + + link = {} + link["ref"] = 32 + link["direction"] = "in" + link["class"] = "Friends" + + addVideo = {} + addVideo["properties"] = props + addVideo["link"] = link + + imgs_arr = [] + + fd = open("../test_videos/Megamind.avi", 'rb') + imgs_arr.append(fd.read()) + fd.close() + + img_params = {} + + query = {} + query["AddVideo"] = addVideo + + all_queries.append(query) + + response, res_arr = db.query(all_queries, [imgs_arr]) + + self.assertEqual(response[0]["AddEntity"]["status"], 0) + self.assertEqual(response[1]["AddVideo"]["status"], 0) + + def test_findVid_multiple_results(self): + db = vdms.vdms() + db.connect(hostname, port) + + prefix_name = "vid_multiple" + + number_of_inserts = 4 + for i in range(0,number_of_inserts): + props = {} + props["name"] = prefix_name + self.insertVideo(db, props=props) + + constraints = {} + constraints["name"] = ["==", prefix_name] + + results = {} + results["list"] = ["name"] + + img_params = {} + img_params["constraints"] = constraints + + query = {} + query["FindVideo"] = img_params + + all_queries = [] + all_queries.append(query) + + response, vid_arr = db.query(all_queries) + + self.assertEqual(len(vid_arr), number_of_inserts) + self.assertEqual(response[0]["FindVideo"]["status"], 0) + self.assertEqual(response[0]["FindVideo"]["returned"], number_of_inserts) + + def test_findVideoNoBlob(self): + db = vdms.vdms() + db.connect(hostname, port) + + prefix_name = "fvid_no_blob_" + + for i in range(0,2): + props = {} + props["name"] = prefix_name + str(i) + self.insertVideo(db, props=props) + + all_queries = [] + + for i in range(0,2): + constraints = {} + constraints["name"] = ["==", prefix_name + str(i)] + + results = {} + results["blob"] = False + results["list"] = ["name"] + + img_params = {} + img_params["constraints"] = constraints + img_params["results"] = results + + query = {} + query["FindVideo"] = img_params + + all_queries.append(query) + + response, img_array = db.query(all_queries) + + self.assertEqual(response[0]["FindVideo"]["status"], 0) + self.assertEqual(response[1]["FindVideo"]["status"], 0) + self.assertEqual(len(img_array), 0) + + def test_updateVideo(self): + db = vdms.vdms() + db.connect(hostname, port) + + prefix_name = "fvid_update_" + + for i in range(0,2): + props = {} + props["name"] = prefix_name + str(i) + self.insertVideo(db, props=props) + + all_queries = [] + + constraints = {} + constraints["name"] = ["==", prefix_name + str(0)] + + props = {} + props["name"] = "simg_update_0" + + img_params = {} + img_params["constraints"] = constraints + img_params["properties"] = props + + query = {} + query["UpdateVideo"] = img_params + + all_queries.append(query) + + response, img_array = db.query(all_queries) + + self.assertEqual(response[0]["UpdateVideo"]["count"], 1) + self.assertEqual(len(img_array), 0) diff --git a/tests/test_videos/Megamind.avi b/tests/test_videos/Megamind.avi new file mode 100644 index 00000000..86351eb0 Binary files /dev/null and b/tests/test_videos/Megamind.avi differ diff --git a/utils/SConscript b/utils/SConscript index eebf1d82..6c33c145 100644 --- a/utils/SConscript +++ b/utils/SConscript @@ -1,9 +1,9 @@ Import('env') def compileProtoFiles(utils_env): - #Compile .proto file to generate protobuf files (.h and .cc). + #Compile .proto file to generate protobuf files (.h and .cc). - protoQuery = utils_env.Command ( + protoQuery = utils_env.Command ( ['include/protobuf/queryMessage.pb.h', 'src/protobuf/queryMessage.pb.cc', '../client/python/vdms/queryMessage_pb2.py', @@ -16,7 +16,7 @@ def compileProtoFiles(utils_env): utils/include/protobuf/queryMessage.pb.h' ) - protoPMGD = utils_env.Command ( + protoPMGD = utils_env.Command ( ['include/protobuf/pmgdMessages.pb.h', 'src/protobuf/pmgdMessages.pb.cc', ], # TARGET @@ -28,7 +28,7 @@ def compileProtoFiles(utils_env): ) def createAPISchemaString(utils_env): - api_schema = utils_env.Command ( + api_schema = utils_env.Command ( 'include/api_schema/APISchema.h', # $TARGET ['src/api_schema/api_schema.json', 'src/api_schema/createApiString.py'], # $SOURCE @@ -60,14 +60,13 @@ chrono_cc = ['src/chrono/Chrono.cc'] utils_source_files = [comm_cc, protobuf_cc, chrono_cc] utils_env.ParseConfig('pkg-config --cflags --libs protobuf') -utils_env.SharedLibrary('vdms-utils', utils_source_files) +ulib = utils_env.SharedLibrary('vdms-utils', utils_source_files) # Comm Testing comm_test_env = Environment(CPPPATH=['include/comm'], - CXXFLAGS="-std=c++11") + CXXFLAGS="-std=c++11", + LIBS = [ulib, 'pthread', 'gtest'] + ) comm_test_source_files = "test/comm/UnitTests.cc"; -comm_test_env.Program('test/comm/comm_test', comm_test_source_files, - LIBS = ['vdms-utils', 'pthread', 'gtest'], - LIBPATH = ['.'] - ) +comm_test = comm_test_env.Program('test/comm/comm_test', comm_test_source_files) diff --git a/utils/src/api_schema/api_schema.json b/utils/src/api_schema/api_schema.json index c9e4b712..ef33303c 100644 --- a/utils/src/api_schema/api_schema.json +++ b/utils/src/api_schema/api_schema.json @@ -37,19 +37,27 @@ { "$ref": "#/definitions/AddEntityTop" }, { "$ref": "#/definitions/UpdateEntityTop" }, { "$ref": "#/definitions/FindEntityTop" }, + { "$ref": "#/definitions/AddConnectionTop" }, { "$ref": "#/definitions/UpdateConnectionTop" }, { "$ref": "#/definitions/FindConnectionTop" }, + { "$ref": "#/definitions/AddImageTop" }, { "$ref": "#/definitions/UpdateImageTop" }, { "$ref": "#/definitions/FindImageTop" }, + { "$ref": "#/definitions/AddDescriptorSetTop" }, { "$ref": "#/definitions/AddDescriptorTop" }, { "$ref": "#/definitions/ClassifyDescriptorTop" }, { "$ref": "#/definitions/FindDescriptorTop" }, + { "$ref": "#/definitions/AddBoundingBoxTop" }, { "$ref": "#/definitions/UpdateBoundingBoxTop" }, - { "$ref": "#/definitions/FindBoundingBoxTop" } + { "$ref": "#/definitions/FindBoundingBoxTop" }, + + { "$ref": "#/definitions/AddVideoTop" }, + { "$ref": "#/definitions/UpdateVideoTop" }, + { "$ref": "#/definitions/FindVideoTop" } ] }, "uniqueItems": false, @@ -89,11 +97,27 @@ "maximun": 10000 }, - "formatString": { + "imgFormatString": { "type": "string", "enum": ["png", "jpg"] }, + "vidCodecString": { + "type": "string", + "enum": ["xvid", "h264", "h263"] + }, + + "vidContainerString": { + "type": "string", + "enum": ["mp4", "avi", "mov"] + }, + + "unitString": { + "type": "string", + "enum": ["frames", "seconds"] + }, + + "edgeDirectionString": { "type": "string", "enum": ["in", "out", "any"] @@ -147,7 +171,7 @@ "additionalProperties": false }, - "blockOperations": { + "blockImageOperations": { "type": "array", "minItems": 1, "items": { @@ -162,14 +186,40 @@ "uniqueItems": false }, + "blockVideoOperations": { + "type": "array", + "minItems": 1, + "items": { + "anyOf": [ + { "$ref": "#/definitions/operationThreshold" }, + { "$ref": "#/definitions/operationResize" }, + { "$ref": "#/definitions/operationCrop" }, + { "$ref": "#/definitions/operationInterval" } + ] + }, + "uniqueItems": false + }, + // Operations + "operationInterval": { + "type": "object", + "properties": { + "type": { "enum": [ "interval" ] }, + "start": { "$ref": "#/definitions/nonNegativeInt" }, + "stop": { "$ref": "#/definitions/nonNegativeInt" }, + "step": { "$ref": "#/definitions/positiveInt" } + }, + "required": ["type", "start", "stop"], + "additionalProperties": false + }, + "operationThreshold": { "type": "object", "properties": { "type": { "enum": [ "threshold" ] }, "value": { "$ref": "#/definitions/nonNegativeInt" } - }, + }, "required": ["type", "value"], "additionalProperties": false }, @@ -180,7 +230,7 @@ "type": { "enum": [ "resize" ] }, "height": { "$ref": "#/definitions/positiveInt" }, "width": { "$ref": "#/definitions/positiveInt" } - }, + }, "required": ["type", "height", "width"], "additionalProperties": false }, @@ -193,7 +243,7 @@ "y": { "$ref": "#/definitions/nonNegativeInt" }, "height": { "$ref": "#/definitions/positiveInt" }, "width": { "$ref": "#/definitions/positiveInt" } - }, + }, "required": ["type", "x", "y", "height", "width"], "additionalProperties": false }, @@ -349,6 +399,27 @@ "additionalProperties": false }, + "AddVideoTop": { + "properties": { + "AddVideo" : { "type": "object", "$ref": "#/definitions/AddVideo" } + }, + "additionalProperties": false + }, + + "UpdateVideoTop": { + "properties": { + "UpdateVideo" : { "type": "object", "$ref": "#/definitions/UpdateVideo" } + }, + "additionalProperties": false + }, + + "FindVideoTop": { + "properties": { + "FindVideo" : { "type": "object", "$ref": "#/definitions/FindVideo" } + }, + "additionalProperties": false + }, + // Commands "AddEntity": { @@ -427,9 +498,9 @@ "AddImage": { "properties": { "_ref": { "$ref": "#/definitions/refInt" }, - "format": { "$ref": "#/definitions/formatString" }, + "format": { "$ref": "#/definitions/imgFormatString" }, "link": { "$ref": "#/definitions/blockLink" }, - "operations": { "$ref": "#/definitions/blockOperations" }, + "operations": { "$ref": "#/definitions/blockImageOperations" }, "properties": { "type": "object" } }, "additionalProperties": false @@ -438,8 +509,6 @@ "UpdateImage": { "properties": { "_ref": { "$ref": "#/definitions/refInt" }, - //"format": { "$ref": "#/definitions/formatString" }, - //"operations": { "$ref": "#/definitions/blockOperations" }, "properties": { "type": "object" }, "remove_props": { "$ref": "#/definitions/stringArray" }, "unique": { "type": "boolean" }, @@ -452,8 +521,8 @@ "properties": { "_ref": { "$ref": "#/definitions/refInt" }, "link": { "$ref": "#/definitions/blockLink" }, - "operations": { "$ref": "#/definitions/blockOperations" }, - "format": { "$ref": "#/definitions/formatString" }, + "operations": { "$ref": "#/definitions/blockImageOperations" }, + "format": { "$ref": "#/definitions/imgFormatString" }, "constraints": { "type": "object" }, "results": { "$ref": "#/definitions/blockResults" }, "unique": { "type": "boolean" } @@ -538,14 +607,52 @@ "image": { "$ref": "#/definitions/refInt" }, "link": { "$ref": "#/definitions/blockLink" }, "constraints": { "type": "object" }, - "format": { "$ref": "#/definitions/formatString" }, + "format": { "$ref": "#/definitions/imgFormatString" }, "results": { "$ref": "#/definitions/blockResults" }, "unique": { "type": "boolean" } }, "not": { "anyOf": [ {"required": ["image", "link"] } ] }, + "additionalProperties": false + }, + + "AddVideo": { + "properties": { + "_ref": { "$ref": "#/definitions/refInt" }, + "codec": { "$ref": "#/definitions/vidCodecString" }, + "container": { "$ref": "#/definitions/vidContainerString" }, + "link": { "$ref": "#/definitions/blockLink" }, + "operations": { "$ref": "#/definitions/blockVideoOperations" }, + "properties": { "type": "object" } + }, + "additionalProperties": false + }, + + "UpdateVideo": { + "properties": { + "_ref": { "$ref": "#/definitions/refInt" }, + "properties": { "type": "object" }, + "remove_props": { "$ref": "#/definitions/stringArray" }, + "constraints": { "type": "object" } + }, + "additionalProperties": false + }, + + "FindVideo": { + "properties": { + "_ref": { "$ref": "#/definitions/refInt" }, + "link": { "$ref": "#/definitions/blockLink" }, + "operations": { "$ref": "#/definitions/blockVideoOperations" }, + "codec": { "$ref": "#/definitions/vidCodecString" }, + "container": { "$ref": "#/definitions/vidContainerString" }, + "constraints": { "type": "object" }, + "results": { "$ref": "#/definitions/blockResults" }, + "unique": { "type": "boolean" } + }, + "additionalProperties": false } + } }