commit a61dda4612988e4e6d7faf98a899de0641df5e52 Author: ascendhuawei Date: Wed Sep 16 11:50:53 2020 -0700 upload diff --git a/README.md b/README.md new file mode 100644 index 0000000..d1cc34b --- /dev/null +++ b/README.md @@ -0,0 +1,193 @@ +# Body Pose Detection \(Python) + +This application runs on Atlas 200 DK, to infer human body poses. The model used is modified from Lightweight OpenPose [Osokin, Daniil. "Real-time 2d multi-person pose estimation on CPU: Lightweight OpenPose." arXiv preprint arXiv:1811.12004 (2018)], an open-source pose detection network. This application can be run on various input formats, namely image input, video input as well as live camera input. + +The pose detection network originates from [Cao, Zhe, et al. "OpenPose: realtime multi-person 2D pose estimation using Part Affinity Fields." arXiv preprint arXiv:1812.08008 (2018)], where the network provides two outputs: heatmaps and PAF maps for each frame. In the post-processing of the model, both heatmaps and PAF maps are used to obtain multi-persion pose estimation. + +In this repository, the post-processing a simplied version for edge computing, where only the heatmap is used to calculate the predicted locations of the human body joints. A set of 14 detected joints are grouped, connected to form limbs and associated to a person. The diagram below represents connection scheme: + + 12 12: head, 13: neck + | + | + 0-----13-----3 + / / \ \ + 1 / \ 4 + / / \ \ + 2 6 9 5 + | | + 7 10 + | | + 8 11 + + + +The figure below shows the sample output on a single image input. The detected pose is displayed in the form of a skeleton overlay on the image. + +**Figure** OpenPose detection result + ![](figures/pose_detected.jpg "pose-detected") + +## Software Preparation + +Clone or download the project repository: + +**mkdir -p $HOME/AscendProjects** +**cd $HOME/AscendProjects** +**git clone ssh://git@rnd-gitlab-ca-g.huawei.com:2222/hispark/openpose-pythonc73.git** +OR +**wget https://rnd-gitlab-ca-g.huawei.com/hispark/openpose-pythonc73.git** + +Then, unzip the downloaded file: + +**unzip openpose-pythonC73.zip** + + +## Environment Preparation +Install required libraries for Python3 environment (OpenCV, PresentAgent and Python3env) + +Please refer to https://github.com/Huawei-Ascend/samples/tree/master/common + + +## Environment Deployment + +1. Go to the root directory where the application code is located, such as: $HOME/AscendProjects/openpose-pythonC73/. + + **cd $HOME/AscendProjects/openpose-pythonC73/** + +2. Modify the configuration file. + + Modify **presenter\_server\_ip** and **presenter\_view\_ip** in **script/body\_pose.conf** to the current ubuntu server and atlas200dk development board network port ip, **presenter \_agent\_ip** is the ip of the network port connected to the ubuntu server on the development board. + + If you use USB connection, the USB network port ip of the development board is 192.168.1.2, and the network port ip of the virtual network card connected to the ubuntu server and the development board is 192.168.1.223, then the configuration file content is as follows: + + **presenter\_server\_ip=192.168.1.223** + + **presenter\_view\_ip=192.168.1.223** + + **presenter\_agent\_ip=192.168.1.2** + + + Generally, when connecting via USB, atlas200dk\_board\_ip is the USB network port ip of the development board, and the default is 192.168.1.2. When connecting through a network port, atlas200dk\_board\_ip is the network port ip of the development board, and the default is 192.168.0.2. + +3. Copy the application code to the development board. + + Navigate to the root directory where the openpose-pythonC73 application code is located, such as: AscendProjects/openpose-pythonC73, execute the following command to copy the application code to the development board. If the copy fails, please check if there is a directory HIAI\_PROJECTS on the development board, and if not, create it. + + **scp -r ~/AscendProjects/openpose-pythonC73 HwHiAiUser@192.168.1.2:/home/HwHiAiUser/HIAI\_PROJECTS** + + Enter the development board password when prompted for password. The default password of the development board is **Mind@123**, as shown below: + + + ![](figures/files_copy.png) + + + +4. Start Presenter Server. + + **NOTE**: This step is required when the presenter server needs to be used for video or live display on the browser. Otherwise, skip this step. + + Execute the following command to start the Presenter Server in the background. + + **bash $HOME/AscendProjects/openpose-pythonC73/script/run_presenter_server.sh &** + + Log in to the Presenter Server using the prompted URL. The figure below shows that the Presenter Server has started successfully. + + **Figure** Home Page Display +![](figures/主页显示.png "Home page display") + + + The communication between Presenter Server, Mind Studio and Atlas 200 DK is shown as below: + + + **Figure** Examples of IP addresses +![](figures/IP地址示例.png "Examples of IP addresses") + + NOTE: + + - The IP address used by the Atlas 200 DK developer board is 192.168.1.2 (USB connection). + - The IP address for the communication between Presenter Server and Atlas 200 DK is the IP address of the UI Host server on the same network segment as Atlas 200 DK, for example: 192.168.1.223. + - This example of the IP address for accessing Presenter Server through a browser is 10.10.0.1. Since Presenter Server and Mind Studio are deployed on the same server, this IP address is also the IP for accessing Mind Studio through a browser. + + +5. Copy acl.so to the development board. Please skip this step if it is already done in other projects. + + **scp ~/Ascend/ascend-toolkit/20.0.RC1/arm64-linux_gcc7.3.0/pyACL/python/site-packages/acl/acl.so HwHiAiUser@192.168.1.2:/home/HwHiAiUser/Ascend/** + + **Please replace X.X.X with the actual version number of the Ascend-Toolkit development kit package** + **For example: the package name of the Toolkit package is Ascend-Toolkit-20.0.RC1-x86_64-linux_gcc7.3.0.run, then the version number of this Toolkit is 20.0.RC1.** + + +6. Log in to the development board and add environment variables. Please skip this step if it is already done in other projects. + + **ssh HwHiAiUser@192.168.1.2** + **vim ~/.bashrc** + Add two lines at the end + **export LD_LIBRARY_PATH=/home/HwHiAiUser/Ascend/acllib/lib64** + **export PYTHONPATH=/home/HwHiAiUser/Ascend/:\\${PYTHONPATH}** + ![](figures/bashrc.png) + + Execute the following command to make the environment variable take effect + **source ~/.bashrc** + + +## Running the Application +1. Check whether the "CAMERA0" camera is used. + The "CAMERA0" camera is used by default in the code. Please refer to the link below for the viewing method. + https://support.huaweicloud.com/usermanual-A200dk_3000/atlas200dk_02_0051.html + +2. Log in to the development board. Navigate to the code directory corresponding to the required input source format (image, video or live camera), and execute one of the following commands to run the application according to the input source format. + + **NOTE**: To execute it, you could use the default input (just run **python3 main.py** without parameter), or input parameters as below for input/output paths and model path as indicated in **main.py**. For the video input, you also have the option to save the output as a video or display to presenter server, with the value in parameter 'is_presenter_server' set to False or True, respectively . + + *Image input source*: + + **cd ~/HIAI_PROJECTS/openpose-pythonC73/code_image** + **python3 main.py --model='model/body_pose.om' --frames_input_src='tennis_player.jpg' --output_dir='outputs'** + + *Video input source (output video)*: + + **cd ~/HIAI_PROJECTS/openpose-pythonC73/code_video** + **python3 main.py --model='model/body_pose.om' --frames_input_src='yoga.mp4' --output_dir='outputs' --is_presenter_server=False** + + Video input source (output streamed to presenter server)*: + + **cd ~/HIAI_PROJECTS/openpose-pythonC73/code_video** + **python3 main.py --model='model/body_pose.om' --frames_input_src='yoga.mp4' --output_dir='outputs' --is_presenter_server=True** + + *Live camera source*: + + **cd ~/HIAI_PROJECTS/openpose-pythonC73/code_live** + **python3 main.py --model='model/body_pose.om'** + +3. If you need to view the detection results using presenter server for the live input or video source, log in to the Presenter Server website using the URL that was prompted when the Presenter Server service was started. Otherwise, skip this step. + +Wait for the Presenter Agent to transmit data to the server, and click "Refresh" to refresh. When there is data, the status of the corresponding Channel turns green, as shown in the figure below. + +**Figure** Presenter Server +![](figures/presenter.png "Presenter-Server-interface") + +Click the corresponding View Name link on the right, such as "video" in the above picture, to view the results. + +## Stopping Application + +If the presenter server is being used for display, stop + +- **Stop Presenter Server** + + The Presenter Server service will always be running after it is started. If you want to stop the Presenter Server service corresponding to the pose detection application, you can perform the following operations. + + Execute the following command on the command line on the server where the process of the Presenter Server service is running: + + **ps -ef | grep presenter** + + ``` + ascend@ubuntu:~/AscendProjects/openpose-pythonC73/script$ ps -ef | grep presenter + ascend 9560 1342 0 02:19 pts/4 00:00:04 python3/home/ascend/AscendProjects/openpose-pythonC73.bak/script/..//present + erserver/presenter_server.py --app openpose-pythonC73 + ``` + + As shown above, _9650_ is the process ID of the Presenter Server service corresponding to the openpose-pythonC73 application. + + If you want to stop this service, execute the following command: + + + **kill -9** _9650_ diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/acl_dvpp.py b/acl_dvpp.py new file mode 100644 index 0000000..aa8ce6e --- /dev/null +++ b/acl_dvpp.py @@ -0,0 +1,215 @@ +import numpy as np +import acl +from atlas_utils.utils import * +from atlas_utils.acl_image import AclImage + +class Dvpp(): + def __init__(self, acl_resource): + self._stream = acl_resource.stream + self._run_mode = acl_resource.run_mode + self._dvpp_channel_desc = None + ret = self._init_resource() + if ret == FAILED: + raise Exception("Dvpp init failed") + + def __del__(self): + if self._resize_config: + acl.media.dvpp_destroy_resize_config(self._resize_config) + + if self._dvpp_channel_desc: + acl.media.dvpp_destroy_channel(self._dvpp_channel_desc) + acl.media.dvpp_destroy_channel_desc(self._dvpp_channel_desc) + + if self._jpege_config: + acl.media.dvpp_destroy_jpege_config(self._jpege_config) + + def _init_resource(self): + # create dvpp channel + self._dvpp_channel_desc = acl.media.dvpp_create_channel_desc() + ret = acl.media.dvpp_create_channel(self._dvpp_channel_desc) + if ret != ACL_ERROR_NONE: + print("Dvpp create channel failed") + return FAILED + # create resize configuration + self._resize_config = acl.media.dvpp_create_resize_config() + #create yuv to jpeg configuration + self._jpege_config = acl.media.dvpp_create_jpege_config() + ret = acl.media.dvpp_set_jpege_config_level (self._jpege_config, 100) + if ret != ACL_ERROR_NONE: + print("Dvpp set jpege config failed") + return FAILED + + return SUCCESS + + def _gen_input_pic_desc(self, image, + width_align_factor=16, height_align_factor=2): + #create image input desc + stride_width = align_up(image.width, width_align_factor) + stride_height = align_up(image.height, height_align_factor) + + pic_desc = acl.media.dvpp_create_pic_desc() + acl.media.dvpp_set_pic_desc_data(pic_desc, image.data()) + acl.media.dvpp_set_pic_desc_format(pic_desc, + PIXEL_FORMAT_YUV_SEMIPLANAR_420) + acl.media.dvpp_set_pic_desc_width(pic_desc, image.width) + acl.media.dvpp_set_pic_desc_height(pic_desc, image.height) + acl.media.dvpp_set_pic_desc_width_stride(pic_desc, stride_width) + acl.media.dvpp_set_pic_desc_height_stride(pic_desc, stride_height) + acl.media.dvpp_set_pic_desc_size(pic_desc, image.size) + + return pic_desc + + def _gen_output_pic_desc(self, width, height, + output_buffer, output_buffer_size, + width_align_factor=16, height_align_factor=2): + # create image output desc + stride_width = align_up(width, width_align_factor) + stride_height = align_up(height, height_align_factor) + + pic_desc = acl.media.dvpp_create_pic_desc() + acl.media.dvpp_set_pic_desc_data(pic_desc, output_buffer) + acl.media.dvpp_set_pic_desc_format(pic_desc, + PIXEL_FORMAT_YUV_SEMIPLANAR_420) + acl.media.dvpp_set_pic_desc_width(pic_desc, width) + acl.media.dvpp_set_pic_desc_height(pic_desc, height) + acl.media.dvpp_set_pic_desc_width_stride(pic_desc, stride_width) + acl.media.dvpp_set_pic_desc_height_stride(pic_desc, stride_height) + acl.media.dvpp_set_pic_desc_size(pic_desc, output_buffer_size) + + return pic_desc + + def _stride_yuv_size(self, width, height, + width_align_factor=16, height_align_factor=2): + stride_width = align_up(width, width_align_factor) + stride_height = align_up(height, height_align_factor) + stride_size = yuv420sp_size(stride_width, stride_height) + + return stride_width, stride_height, stride_size + + + def jpegd(self, image): + # jepg image to yuv image + # create converted image desc + output_desc, out_buffer = self._gen_jpegd_out_pic_desc(image) + ret = acl.media.dvpp_jpeg_decode_async(self._dvpp_channel_desc, + image.data(), + image.size, + output_desc, + self._stream) + if ret != ACL_ERROR_NONE: + print("dvpp_jpeg_decode_async failed ret={}".format(ret)) + return None + + ret = acl.rt.synchronize_stream(self._stream) + if ret != ACL_ERROR_NONE: + print("dvpp_jpeg_decode_async failed ret={}".format(ret)) + return None + + width, height, size = self._stride_yuv_size(image.width, image.height) + return AclImage(out_buffer, width, height, size) + + def _gen_jpegd_out_pic_desc(self, image): + # predict memory sieze for decoding jpeg to yuy image + out_buffer_size, ret = acl.media.dvpp_jpeg_predict_dec_size( \ + image.data(), image.size, PIXEL_FORMAT_YUV_SEMIPLANAR_420) + if ret != ACL_ERROR_NONE: + print("Predict jpeg decode size failed, return ", ret) + return None + # allocate memory for yuv image + out_buffer, ret = acl.media.dvpp_malloc(out_buffer_size) + if ret != ACL_ERROR_NONE: + print("Dvpp malloc failed, error: ", ret) + return None + # create output image desc + pic_desc = self._gen_output_pic_desc(image.width, image.height, + out_buffer, out_buffer_size) + return pic_desc, out_buffer + + + def resize(self, image, resize_width, resize_height): + # resize yuvsp420 image to specified size + # generate input image desc + input_desc = self._gen_input_pic_desc(image) + # calculate resized image size + stride_width = align_up16(resize_width) + stride_height = align_up2(resize_height) + output_size = yuv420sp_size(stride_width, stride_height) + # allocate memory for resized image + out_buffer, ret = acl.media.dvpp_malloc(output_size) + if ret != ACL_ERROR_NONE: + print("Dvpp malloc failed, error: ", ret) + return None + #create output desc + output_desc = self._gen_output_pic_desc(resize_width, resize_height, + out_buffer, output_size) + if output_desc == None: + print("Gen resize output desc failed") + return None + # call DVPP asynchronous resize interface to resize image + ret = acl.media.dvpp_vpc_resize_async(self._dvpp_channel_desc, + input_desc, + output_desc, + self._resize_config, + self._stream) + if ret != ACL_ERROR_NONE: + print("Vpc resize async failed, error: ", ret) + return None + # wait for resize to complete + ret = acl.rt.synchronize_stream(self._stream) + if ret != ACL_ERROR_NONE: + print("Resize synchronize stream failed, error: ", ret) + return None + # release allocated memory for resize + acl.media.dvpp_destroy_pic_desc(input_desc) + acl.media.dvpp_destroy_pic_desc(output_desc) + return AclImage(out_buffer, stride_width, + stride_height, output_size, MEMORY_DVPP) + + def _gen_resize_out_pic_desc(self, resize_width, + resize_height, output_size): + out_buffer, ret = acl.media.dvpp_malloc(output_size) + if ret != ACL_ERROR_NONE: + print("Dvpp malloc failed, error: ", ret) + return None + pic_desc = self._gen_output_pic_desc(resize_width, resize_height, + out_buffer, output_size) + return pic_desc, out_buffer + + + def jpege(self, image): + # convert yuv420sp image to jpeg image + # create input image desc + input_desc = self._gen_input_pic_desc(image) + # predict memory size for conversion + output_size, ret = acl.media.dvpp_jpeg_predict_enc_size( + input_desc, self._jpege_config) + if (ret != ACL_ERROR_NONE): + print("Predict jpege output size failed") + return None + # allocate memory for conversion + output_buffer, ret = acl.media.dvpp_malloc(output_size) + if (ret != ACL_ERROR_NONE): + print("Malloc jpege output memory failed") + return None + # output size is an parameter for both input and output, which needs to be a pointer + output_size_array = np.array([output_size], dtype=np.int32) + output_size_ptr = acl.util.numpy_to_ptr(output_size_array) + + # call jpeg asynchronous interface to convert image异步接口转换图片 + ret = acl.media.dvpp_jpeg_encode_async(self._dvpp_channel_desc, + input_desc, output_buffer, + output_size_ptr, + self._jpege_config, + self._stream) + if (ret != ACL_ERROR_NONE): + print("Jpege failed, ret ", ret) + return None + # wait for conversion to complete + ret = acl.rt.synchronize_stream(self._stream) + if (ret != ACL_ERROR_NONE): + print("Jpege synchronize stream, failed, ret ", ret) + return None + # release resource + acl.media.dvpp_destroy_pic_desc(input_desc) + return AclImage(output_buffer, image.width, + image.height, int(output_size_array[0]), MEMORY_DVPP) diff --git a/acl_model.py b/acl_model.py new file mode 100644 index 0000000..f45b407 --- /dev/null +++ b/acl_model.py @@ -0,0 +1,263 @@ +import acl +import numpy as np +import datetime +from atlas_utils.utils import * +from atlas_utils.acl_image import AclImage + + +class Model(object): + def __init__(self, acl_resource, model_path): + self._run_mode = acl_resource.run_mode + self.model_path = model_path # string + self.model_id = None # pointer + self.input_dataset = None + self.output_dataset = None + self._output_info = [] + self.model_desc = None # pointer when using + self._init_resource() + + + def __del__(self): + if self.input_dataset: + self._release_dataset(self.input_dataset) + if self.output_dataset: + self._release_dataset(self.output_dataset) + if self.model_id: + ret = acl.mdl.unload(self.model_id) + if ret != ACL_ERROR_NONE: + print("acl.mdl.unload error:", ret) + if self.model_desc: + ret = acl.mdl.destroy_desc(self.model_desc) + if ret != ACL_ERROR_NONE: + print("acl.mdl.destroy_desc error:", ret) + print("Model release source success") + + def _init_resource(self): + print("Init model resource") + # loading model file + self.model_id, ret = acl.mdl.load_from_file(self.model_path) + check_ret("acl.mdl.load_from_file", ret) + self.model_desc = acl.mdl.create_desc() + ret = acl.mdl.get_desc(self.model_desc, self.model_id) + check_ret("acl.mdl.get_desc", ret) + # obtain model outputs number + output_size = acl.mdl.get_num_outputs(self.model_desc) + # create model output dataset structure + self._gen_output_dataset(output_size) + print("[Model] class Model init resource stage success") + # obtain shape and type of each output of the model + self._get_output_desc(output_size) + # create a table to record memory for input data, which can be reused when allocating memory for the input + self._init_input_buffer() + + return SUCCESS + + def _get_output_desc(self, output_size): + for i in range(output_size): + # obtain shape and type of each output + dims = acl.mdl.get_output_dims(self.model_desc, i) + shape = tuple(dims[0]["dims"]) + datatype = acl.mdl.get_output_data_type(self.model_desc, i) + size = acl.mdl.get_output_size_by_index(self.model_desc, i) + + if datatype == ACL_FLOAT: + np_type = np.float32 + elif datatype == ACL_INT32: + np_type = np.int32 + elif datatype == ACL_UINT32: + np_type = np.uint32 + else: + print("Unspport model output datatype ", datatype) + return None + # create a numpy array corresponding to outputs, with the same datatype and shape of outputs + output_tensor = np.zeros(size//4, dtype=np_type).reshape(shape) + if not output_tensor.flags['C_CONTIGUOUS']: + output_tensor = np.ascontiguousarray(output_tensor) + + tensor_ptr = acl.util.numpy_to_ptr(output_tensor) + self._output_info.append({"ptr": tensor_ptr, + "tensor": output_tensor}) + + def _gen_output_dataset(self, size): + print("[Model] create model output dataset:") + dataset = acl.mdl.create_dataset() + for i in range(size): + # allocate device memory for each output + size = acl.mdl.get_output_size_by_index(self.model_desc, i) + buffer, ret = acl.rt.malloc(size, ACL_MEM_MALLOC_NORMAL_ONLY) + check_ret("acl.rt.malloc", ret) + # create output data buffer structure, fill allocated memory into the data buffer + dataset_buffer = acl.create_data_buffer(buffer, size) + #add data buffer to output dataset + _, ret = acl.mdl.add_dataset_buffer(dataset, dataset_buffer) + print("malloc output %d, size %d"%(i, size)) + if ret: + # release resource if failed + acl.rt.free(buffer) + acl.destroy_data_buffer(dataset) + check_ret("acl.destroy_data_buffer", ret) + self.output_dataset = dataset + print("[Model] create model output dataset success") + + def _init_input_buffer(self): + # create a table recording the input data memory allocated for users + # currently, the numpy data needs to be copied to device for memory allocation only when the input is numpy array + self._input_num = acl.mdl.get_num_inputs(self.model_desc) + self._input_buffer = [] + for i in range(self._input_num): + # none of inputs is allocated memory initially + item = {"addr":None, "size":0} + self._input_buffer.append(item) + + def _gen_input_dataset(self, input_list): + # organize input dataset structure + ret = SUCCESS + # return if the input number does not match model requirements + if len(input_list) != self._input_num: + print("Current input data num %d unequal to" + " model input num %d"%(len(input_list), self._input_num)) + return FAILED + + self.input_dataset = acl.mdl.create_dataset() + for i in range(self._input_num): + item = input_list[i] + # parse input, currently supports AclImage type, Acl pointer and numpy array + data, size = self._parse_input_data(item, i) + if (data is None) or (size == 0): + # not parse the remaining data when parsing data fails + ret = FAILED + print("The %d input is invalid"%(i)) + break + # create input dataset buffer structure, fill in input data + dataset_buffer = acl.create_data_buffer(data, size) + # add dataset buffer to dataset + _, ret = acl.mdl.add_dataset_buffer(self.input_dataset, + dataset_buffer) + if ret: + print("Add input dataset buffer failed") + acl.destroy_data_buffer(self.input_dataset) + ret = FAILED + break + if ret == FAILED: + # release dataset if fails + self._release_dataset(self.input_dataset) + + return ret + + def _parse_input_data(self, input, index): + data = None + size = 0 + if isinstance(input, AclImage): + # if input data is AclImage, directly return memory pointer and sieze of image + # defautly image memory is data on the device + size = input.size + data = input.data() + elif isinstance(input, np.ndarray): + # if input is numpy data, allocate device memory for data and copy data to device + # allocated memory can be reused, no need to apply for it everytime + ptr = acl.util.numpy_to_ptr(input) + size = input.size * input.itemsize + data = self._copy_input_to_device(ptr, size, index) + if data == None: + size = 0 + print("Copy input to device failed") + # if directly input memory pointer, structure should be dict like {"data":, "size":}, and default memory is on the device side + elif (isinstance(input, dict) and + input.has_key('data') and input.has_key('size')): + size = input['size'] + data = input['data'] + else: + print("Unsupport input") + + return data, size + + def _copy_input_to_device(self, input_ptr, size, index): + # allocate device memory for input, and copy data to this memory + buffer_item = self._input_buffer[index] + data = None + # according to the index of the data in the model input, check whether the input has already been allocated memory + if buffer_item['addr'] is None: + # if not, allocate memory, copy data and record memory for resue + data = copy_data_device_to_device(input_ptr, size) + if data is None: + print("Malloc memory and copy model %dth " + "input to device failed"%(index)) + return None + buffer_item['addr'] = data + buffer_item['size'] = size + elif size == buffer_item['size']: + # if memory has already been allocated for the input, and memory size is consistent with current input data + # copy data to this memory for inference + ret = acl.rt.memcpy(buffer_item['addr'], size, + input_ptr, size, + ACL_MEMCPY_DEVICE_TO_DEVICE) + if ret != ACL_ERROR_NONE: + print("Copy model %dth input to device failed"%(index)) + return None + data = buffer_item['addr'] + else: + # if memory has already been allocated for the input, but memory size is not consistent with current input data + # it would be considered as exception. because size of each model input is fixed + print("The model %dth input size %d is change," + " before is %d"%(index, size, buffer_item['size'])) + return None + + return data + + def execute(self, input_list): + # create dataset object instance for offline model inference + ret = self._gen_input_dataset(input_list) + if ret == FAILED: + print("Gen model input dataset failed") + return None + # call execute inference data of offline model + start = datetime.datetime.now() + ret = acl.mdl.execute(self.model_id, + self.input_dataset, + self.output_dataset) + if ret != ACL_ERROR_NONE: + print("Execute model failed for acl.mdl.execute error ", ret) + return None + end = datetime.datetime.now() + print("acl.mdl.execute exhaust ", end - start) + # release input dataset object instance without releasing input data memory + #self._release_dataset(self.input_dataset) + # decode the binary data stream output from inference to numpy array, shape and datatype of the array are consistent with model outputs + return self._output_dataset_to_numpy() + + def _output_dataset_to_numpy(self): + dataset = [] + num = acl.mdl.get_dataset_num_buffers(self.output_dataset) + # iterative each output + for i in range(num): + # obtain memory address from output buffer + buffer = acl.mdl.get_dataset_buffer(self.output_dataset, i) + data = acl.get_data_buffer_addr(buffer) + size = int(acl.get_data_buffer_size(buffer)) + output_ptr = self._output_info[i]["ptr"] + output_tensor = self._output_info[i]["tensor"] + ret = acl.rt.memcpy(output_ptr, output_tensor.size*output_tensor.itemsize, + data, size, ACL_MEMCPY_DEVICE_TO_DEVICE) + if ret != ACL_ERROR_NONE: + print("Memcpy inference output to local failed") + return None + + dataset.append(output_tensor) + + return dataset + + def _release_dataset(self, dataset): + if not dataset: + return + print("destroy dataset") + num = acl.mdl.get_dataset_num_buffers(dataset) + for i in range(num): + data_buf = acl.mdl.get_dataset_buffer(dataset, i) + if data_buf: + ret = acl.destroy_data_buffer(data_buf) + if ret != ACL_ERROR_NONE: + print("Destroy data buffer error ", ret) + ret = acl.mdl.destroy_dataset(dataset) + if ret != ACL_ERROR_NONE: + print("Destroy data buffer error ", ret) + diff --git a/acl_resource.py b/acl_resource.py new file mode 100644 index 0000000..28c453a --- /dev/null +++ b/acl_resource.py @@ -0,0 +1,38 @@ +import acl + +from atlas_utils.utils import * + +class AclResource(object): + def __init__(self, device_id=0): + self.device_id = device_id + self.context = None + self.stream = None + self.run_mode = None + + def init(self): + print("init resource stage:") + ret = acl.init() + check_ret("acl.rt.set_device", ret) + + ret = acl.rt.set_device(self.device_id) + check_ret("acl.rt.set_device", ret) + + self.context, ret = acl.rt.create_context(self.device_id) + check_ret("acl.rt.create_context", ret) + + self.stream, ret = acl.rt.create_stream() + check_ret("acl.rt.create_stream", ret) + + self.run_mode, ret = acl.rt.get_run_mode() + check_ret("acl.rt.get_run_mode", ret) + + print("Init resource success") + + def __del__(self): + if self.stream: + acl.rt.destroy_stream(self.stream) + if self.context: + acl.rt.destroy_context(self.context) + acl.rt.reset_device(self.device_id) + acl.finalize() + print("Release acl resource success") diff --git a/atlas.log b/atlas.log new file mode 100644 index 0000000..e69de29 diff --git a/atlas_utils/__init__.py b/atlas_utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/atlas_utils/acl_image.py b/atlas_utils/acl_image.py new file mode 100644 index 0000000..ffd3f61 --- /dev/null +++ b/atlas_utils/acl_image.py @@ -0,0 +1,115 @@ +import numpy as np +from PIL import Image +import copy + +import acl +#from utils import * +from atlas_utils.constants import * + + +class AclImage(): + def __init__(self, image, width=0, height=0, + size=0, memory_type=MEMORY_NORMAL): + self._data = None + self._np_array = None + self._memory_type = memory_type + self.width = 0 + self.height = 0 + self.channels = 0 + self.size = 0 + + if isinstance(image, str): + self._instance_by_image_file(image) + elif str(type(image))=="": + self._instance_by_numpy_array(image,width,height) + elif isinstance(image, int): + self._instance_by_buffer(image, width, height, size) + else: + print("Create instance failed for unknow image data type") + + def _instance_by_numpy_array(self, image,width,height): + self._data = image + self._type = IMAGE_DATA_NUMPY + self.size = self._data.itemsize * self._data.size + self.width = width + self.height = height + + def _instance_by_image_file(self, image_path): + self._data = np.fromfile(image_path, dtype=np.byte) + self._type = IMAGE_DATA_NUMPY + self.size = self._data.itemsize * self._data.size + image = Image.open(image_path) + self.width, self.height = image.size + + def _instance_by_buffer(self, image_buffer, width, height, size): + self.width = width + self.height = height + self.size = size + self._data = image_buffer + self._type = IMAGE_DATA_BUFFER + + def tobytes(self): + if self._type == IMAGE_DATA_NUMPY: + return self._data + else: + return acl.util.ptr_to_numpy(self._data, (self.size, ), NPY_BYTE).tobytes() + + def data(self): + if self._type == IMAGE_DATA_NUMPY: + return acl.util.numpy_to_ptr(self._data) + else: + return self._data + + def copy_to_device(self, run_mode): + device_ptr = None + if run_mode == ACL_HOST: + device_ptr = copy_data_host_to_device(self.data(), self.size) + else: + device_ptr = copy_data_device_to_device(self.data(), self.size) + if device_ptr is None: + print("Copy image to device failed ") + return None + + print("image copyt to device ", device_ptr, "%d, %d, %d"%(self.width, + self.height, self.size)) + return AclImage(device_ptr, self.width, + self.height, self.size, MEMORY_DEVICE) + + def copy_as_nparray(self): + if self._type == IMAGE_DATA_BUFFER: + #np_output = np.zeros(self.size, dtype=np.byte) + #if not np_output.flags['C_CONTIGUOUS']: + # np_output = np.ascontiguousarray(np_output) + np_output_ptr, ret = acl.rt.malloc(self.size, ACL_MEM_MALLOC_NORMAL_ONLY) + print("image ", np_output_ptr) + ret = acl.rt.memcpy(np_output_ptr, self.size, self._data, self.size, 3) + if (ret != ACL_ERROR_NONE): + print("Copy mage to np array failed for memcpy error ", ret) + return None + return copy.deepcopy(acl.util.ptr_to_numpy(np_output_ptr, (self.size, ), NPY_BYTE)) + else: + return self._data.copy() + + def destroy(self): + if (self._data is None) or (self.size == 0): + print("Release image abnormaly, data is None") + return + + if self._memory_type == MEMORY_DEVICE: + acl.rt.free(self._data) + elif self._memory_type == MEMORY_HOST: + acl.rt.free_host(self._data) + elif self._memory_type == MEMORY_DVPP: + acl.media.dvpp_free(self._data) + + self._data = None + self.size = 0 + + def __del__(self): + self.destroy() + + + + + + diff --git a/atlas_utils/camera.py b/atlas_utils/camera.py new file mode 100644 index 0000000..9bcbf74 --- /dev/null +++ b/atlas_utils/camera.py @@ -0,0 +1,83 @@ +# !/usr/bin/env python +# -*- coding:utf-8 -*- +# +from ctypes import * +import os +import time +import sys +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(BASE_DIR) + +from lib.atlasutil_so import libatlas +from constants import * +from acl_image import AclImage + +CAMERA_OK = 0 +CAMERA_ERROR = 1 + +CAMERA_CLOSED = 0 +CAMERA_OPENED = 1 + +INVALID_IMAGE_PTR = 0 + +class CameraOutputC(Structure): + _fields_ = [ + ('size', c_int), + ('data', POINTER(c_ubyte)) + ] + + +class Camera(): + def __init__(self, id, fps=20, size=(1280, 720)): + self._id = id + self._fps = fps + self._width = size[0] + self._height = size[1] + self._size = int(self._width * self._height * 3 / 2) + self._status = CAMERA_CLOSED + if CAMERA_OK == self._open(): + self._status = CAMERA_OPENED + else: + print("Open camera %d failed"%(id)) + + def _open(self): + ret = libatlas.OpenCameraEx(self._id, self._fps, + self._width, self._height) + print(ret) + if (ret != CAMERA_OK): + print("ERROR:Open camera %d failed ,ret = %d"%(self._id, ret)) + return CAMERA_ERROR + self._status = CAMERA_OPENED + return CAMERA_OK + + def is_opened(self): + return (self._status == CAMERA_OPENED) + + def read(self): + frame_data = CameraOutputC() + ret = libatlas.ReadCameraFrame(self._id, byref(frame_data)) + if (ret != CAMERA_OK): + print("ERROR:Read camera %d failed"%(self._id)) + return None + + return AclImage(addressof(frame_data.data.contents), + self._width, self._height, self._size, MEMORY_DVPP) + + def close(self): + print("Close camera ", self._id) + libatlas.CloseCameraEx(self._id) + + def __del__(self): + self.close() + + +if __name__ == "__main__": + cap = Camera(id=0, fps=20, size=(1280, 720)) + + start = time.time() + for i in range(0,100): + image = cap.read() + print("Read 100 frame exhaust ", time.time() - start) + + + diff --git a/atlas_utils/constants.py b/atlas_utils/constants.py new file mode 100644 index 0000000..8559c39 --- /dev/null +++ b/atlas_utils/constants.py @@ -0,0 +1,188 @@ +""" +Copyright (R) @huawei.com, all rights reserved +-*- coding:utf-8 -*- +CREATED: 2020-6-04 20:12:13 +MODIFIED: 2020-6-06 14:04:45 +""" +SUCCESS = 0 +FAILED = 1 + +ACL_DEVICE = 0 +ACL_HOST = 1 + +MEMORY_NORMAL = 0 +MEMORY_HOST = 1 +MEMORY_DEVICE = 2 +MEMORY_DVPP = 3 +MEMORY_CTYPES = 4 + +IMAGE_DATA_NUMPY = 0 +IMAGE_DATA_BUFFER = 1 + + +# error code +ACL_ERROR_NONE = 0 +ACL_ERROR_INVALID_PARAM = 100000 +ACL_ERROR_UNINITIALIZE = 100001 +ACL_ERROR_REPEAT_INITIALIZE = 100002 +ACL_ERROR_INVALID_FILE = 100003 +ACL_ERROR_WRITE_FILE = 100004 +ACL_ERROR_INVALID_FILE_SIZE = 100005 +ACL_ERROR_PARSE_FILE = 100006 +ACL_ERROR_FILE_MISSING_ATTR = 100007 +ACL_ERROR_FILE_ATTR_INVALID = 100008 +ACL_ERROR_INVALID_DUMP_CONFIG = 100009 +ACL_ERROR_INVALID_PROFILING_CONFIG = 100010 +ACL_ERROR_INVALID_MODEL_ID = 100011 +ACL_ERROR_DESERIALIZE_MODEL = 100012 +ACL_ERROR_PARSE_MODEL = 100013 +ACL_ERROR_READ_MODEL_FAILURE = 100014 +ACL_ERROR_MODEL_SIZE_INVALID = 100015 +ACL_ERROR_MODEL_MISSING_ATTR = 100016 +ACL_ERROR_MODEL_INPUT_NOT_MATCH = 100017 +ACL_ERROR_MODEL_OUTPUT_NOT_MATCH = 100018 +ACL_ERROR_MODEL_NOT_DYNAMIC = 100019 +ACL_ERROR_OP_TYPE_NOT_MATCH = 100020 +ACL_ERROR_OP_INPUT_NOT_MATCH = 100021 +ACL_ERROR_OP_OUTPUT_NOT_MATCH = 100022 +ACL_ERROR_OP_ATTR_NOT_MATCH = 100023 +ACL_ERROR_OP_NOT_FOUND = 100024 +ACL_ERROR_OP_LOAD_FAILED = 100025 +ACL_ERROR_UNSUPPORTED_DATA_TYPE = 100026 +ACL_ERROR_FORMAT_NOT_MATCH = 100027 +ACL_ERROR_BIN_SELECTOR_NOT_REGISTERED = 100028 +ACL_ERROR_KERNEL_NOT_FOUND = 100029 +ACL_ERROR_BIN_SELECTOR_ALREADY_REGISTERED = 100030 +ACL_ERROR_KERNEL_ALREADY_REGISTERED = 100031 +ACL_ERROR_INVALID_QUEUE_ID = 100032 +ACL_ERROR_REPEAT_SUBSCRIBE = 100033 +ACL_ERROR_STREAM_NOT_SUBSCRIBE = 100034 +ACL_ERROR_THREAD_NOT_SUBSCRIBE = 100035 +ACL_ERROR_WAIT_CALLBACK_TIMEOUT = 100036 +ACL_ERROR_REPEAT_FINALIZE = 100037 +ACL_ERROR_BAD_ALLOC = 200000 +ACL_ERROR_API_NOT_SUPPORT = 200001 +ACL_ERROR_INVALID_DEVICE = 200002 +ACL_ERROR_MEMORY_ADDRESS_UNALIGNED = 200003 +ACL_ERROR_RESOURCE_NOT_MATCH = 200004 +ACL_ERROR_INVALID_RESOURCE_HANDLE = 200005 +ACL_ERROR_STORAGE_OVER_LIMIT = 300000 +ACL_ERROR_INTERNAL_ERROR = 500000 +ACL_ERROR_FAILURE = 500001 +ACL_ERROR_GE_FAILURE = 500002 +ACL_ERROR_RT_FAILURE = 500003 +ACL_ERROR_DRV_FAILURE = 500004 +# rule for mem +ACL_MEM_MALLOC_HUGE_FIRST = 0 +ACL_MEM_MALLOC_HUGE_ONLY = 1 +ACL_MEM_MALLOC_NORMAL_ONLY = 2 +# rule for memory copy +ACL_MEMCPY_HOST_TO_HOST = 0 +ACL_MEMCPY_HOST_TO_DEVICE = 1 +ACL_MEMCPY_DEVICE_TO_HOST = 2 +ACL_MEMCPY_DEVICE_TO_DEVICE = 3 +# input +LAST_ONE = -1 +LAST_TWO = -2 +type_dict = { + "bool": 0, + "int8": 1, + "int16": 2, + "int32": 4, + "int64": 8, + "uint8": 1, + "uint16": 2, + "uint32": 4, + "uint64": 8, + "float16": 2, + "float32": 4, + "float64": 8, + "float_": 8 +} +NPY_BOOL = 0 +NPY_BYTE = 1 +NPY_UBYTE = 2 +NPY_SHORT = 3 +NPY_USHORT = 4 +NPY_INT = 5 +NPY_UINT = 6 +NPY_LONG = 7 +NPY_ULONG = 8 +NPY_LONGLONG = 9 +NPY_ULONGLONG = 10 + +ACL_DT_UNDEFINED = -1 +ACL_FLOAT = 0 +ACL_FLOAT16 = 1 +ACL_INT8 = 2 +ACL_INT32 = 3 +ACL_UINT8 = 4 +ACL_INT16 = 6 +ACL_UINT16 = 7 +ACL_UINT32 = 8 +ACL_INT64 = 9 +ACL_UINT64 = 10 +ACL_DOUBLE = 11 +ACL_BOOL = 12 + + + +# data format +ACL_FORMAT_UNDEFINED = -1 +ACL_FORMAT_NCHW = 0 +ACL_FORMAT_NHWC = 1 +ACL_FORMAT_ND = 2 +ACL_FORMAT_NC1HWC0 = 3 +ACL_FORMAT_FRACTAL_Z = 4 +ACL_DT_UNDEFINED = -1 +ACL_FLOAT = 0 +ACL_FLOAT16 = 1 +ACL_INT8 = 2 +ACL_INT32 = 3 +ACL_UINT8 = 4 +ACL_INT16 = 6 +ACL_UINT16 = 7 +ACL_UINT32 = 8 +ACL_INT64 = 9 +ACL_UINT64 = 10 +ACL_DOUBLE = 11 +ACL_BOOL = 12 +acl_dtype = { + "dt_undefined": -1, + "float": 0, + "float16": 1, + "int8": 2, + "int32": 3, + "uint8": 4, + "int16": 6, + "uint16": 7, + "uint32": 8, + "int64": 9, + "double": 11, + "bool": 12 +} +ACL_CALLBACK_NO_BLOCK = 0 +ACL_CALLBACK_BLOCK = 1 +PIXEL_FORMAT_YUV_400 = 0 # 0, YUV400 8bit +PIXEL_FORMAT_YUV_SEMIPLANAR_420 = 1 # 1, YUV420SP NV12 8bit +PIXEL_FORMAT_YVU_SEMIPLANAR_420 = 2 # 2, YUV420SP NV21 8bit +PIXEL_FORMAT_YUV_SEMIPLANAR_422 = 3 # 3, YUV422SP NV12 8bit +PIXEL_FORMAT_YVU_SEMIPLANAR_422 = 4 # 4, YUV422SP NV21 8bit +PIXEL_FORMAT_YUV_SEMIPLANAR_444 = 5 # 5, YUV444SP NV12 8bit +PIXEL_FORMAT_YVU_SEMIPLANAR_444 = 6 # 6, YUV444SP NV21 8bit +PIXEL_FORMAT_YUYV_PACKED_422 = 7 # 7, YUV422P YUYV 8bit +PIXEL_FORMAT_UYVY_PACKED_422 = 8 # 8, YUV422P UYVY 8bit +PIXEL_FORMAT_YVYU_PACKED_422 = 9 # 9, YUV422P YVYU 8bit +PIXEL_FORMAT_VYUY_PACKED_422 = 10 # 10, YUV422P VYUY 8bit +PIXEL_FORMAT_YUV_PACKED_444 = 11 # 11, YUV444P 8bit +PIXEL_FORMAT_RGB_888 = 12 # 12, RGB888 +PIXEL_FORMAT_BGR_888 = 13 # 13, BGR888 +PIXEL_FORMAT_ARGB_8888 = 14 # 14, ARGB8888 +PIXEL_FORMAT_ABGR_8888 = 15 # 15, ABGR8888 +PIXEL_FORMAT_RGBA_8888 = 16 # 16, RGBA8888 +PIXEL_FORMAT_BGRA_8888 = 17 # 17, BGRA8888 +PIXEL_FORMAT_YUV_SEMI_PLANNER_420_10BIT = 18 # 18, YUV420SP 10bit +PIXEL_FORMAT_YVU_SEMI_PLANNER_420_10BIT = 19 # 19, YVU420sp 10bit +PIXEL_FORMAT_YVU_PLANAR_420 = 20 # 20, YUV420P 8bit +# images format +IMG_EXT = ['.jpg', '.JPG', '.png', '.PNG', '.bmp', '.BMP', '.jpeg', '.JPEG'] diff --git a/atlas_utils/lib/__init__.py b/atlas_utils/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/atlas_utils/lib/atlasutil_so.py b/atlas_utils/lib/atlasutil_so.py new file mode 100644 index 0000000..aff4edd --- /dev/null +++ b/atlas_utils/lib/atlasutil_so.py @@ -0,0 +1,20 @@ +import threading +import ctypes +import os + + +class _AtlasutilLib(object): + _instance_lock = threading.Lock() + lib = ctypes.CDLL(os.path.dirname(os.path.abspath(__file__)) + '/libatlasutil.so') + + def __init__(self): + pass + + def __new__(cls, *args, **kwargs): + if not hasattr(_AtlasutilLib, "_instance"): + with _AtlasutilLib._instance_lock: + if not hasattr(_AtlasutilLib, "_instance"): + _AtlasutilLib._instance = object.__new__(cls) + return _AtlasutilLib._instance + +libatlas = _AtlasutilLib.lib diff --git a/atlas_utils/lib/libatlasutil.so b/atlas_utils/lib/libatlasutil.so new file mode 100644 index 0000000..f129db1 Binary files /dev/null and b/atlas_utils/lib/libatlasutil.so differ diff --git a/atlas_utils/lib/libatlasutil.so.bak b/atlas_utils/lib/libatlasutil.so.bak new file mode 100644 index 0000000..97ea0f1 Binary files /dev/null and b/atlas_utils/lib/libatlasutil.so.bak differ diff --git a/atlas_utils/lib/src/Makefile b/atlas_utils/lib/src/Makefile new file mode 100644 index 0000000..b842f90 --- /dev/null +++ b/atlas_utils/lib/src/Makefile @@ -0,0 +1,59 @@ +TOPDIR := $(patsubst %,%,$(CURDIR)) + +ifndef DDK_PATH +$(error "Can not find DDK_PATH env, please set it in environment!.") +endif + +LOCAL_MODULE_NAME := libatlasutil.so +CC := aarch64-linux-gnu-g++ + + +LOCAL_DIR := . +OUT_DIR = out +OBJ_DIR = $(OUT_DIR)/obj +DEPS_DIR = $(OUT_DIR)/deps +LOCAL_LIBRARY=$(OUT_DIR)/$(LOCAL_MODULE_NAME) +OUT_INC_DIR = $(OUT_DIR)/include + +INC_DIR = \ + -I$(HOME)/Ascend/driver/ \ + -I$(DDK_PATH)/../arm64-linux_gcc7.3.0/acllib/include/ + + +CC_FLAGS := $(INC_DIR) -DENABLE_DVPP_INTERFACE -std=c++11 -fPIC -Wall -O2 +LNK_FLAGS := \ + -L$(NPU_HOST_LIB) \ + -L$(HOME)/Ascend/driver \ + -lmedia_mini \ + -lascendcl \ + -lacl_dvpp \ + -shared + +SRCS := $(patsubst $(LOCAL_DIR)/%.cpp, %.cpp, $(shell find $(LOCAL_DIR) -name "*.cpp")) +OBJS := $(addprefix $(OBJ_DIR)/, $(patsubst %.cpp, %.o,$(SRCS))) + +ALL_OBJS := $(OBJS) + +all: do_pre_build do_build + +do_pre_build: + $(Q)echo - do [$@] + $(Q)mkdir -p $(OBJ_DIR) + $(Q)mkdir -p $(OUT_INC_DIR) + +do_build: $(LOCAL_LIBRARY) | do_pre_build + $(Q)echo - do [$@] + +$(LOCAL_LIBRARY): $(ALL_OBJS) + $(Q)echo [LD] $@ + $(Q)$(CC) $(CC_FLAGS) -o $@ $^ -Wl,--whole-archive -Wl,--no-whole-archive -Wl,--start-group -Wl,--end-group -Wl,-rpath='/home/HwHiAiUser/HIAI_PROJECTS/ascend_lib' $(LNK_FLAGS) + + +$(OBJS): $(OBJ_DIR)/%.o : %.cpp | do_pre_build + $(Q)echo [CC] $@ + $(Q)mkdir -p $(dir $@) + $(Q)$(CC) $(CC_FLAGS) $(INC_DIR) -c -fstack-protector-all $< -o $@ + + +clean: + rm -rf $(TOPDIR)/out diff --git a/atlas_utils/lib/src/atlas_utils_common.cpp b/atlas_utils/lib/src/atlas_utils_common.cpp new file mode 100644 index 0000000..0b9f373 --- /dev/null +++ b/atlas_utils/lib/src/atlas_utils_common.cpp @@ -0,0 +1,209 @@ +/** + * ============================================================================ + * + * Copyright (C) 2018, Hisilicon Technologies Co., Ltd. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1 Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2 Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3 Neither the names of the copyright holders nor the names of the + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + */ +#include +#include +#include +#include +#include +#include "acl/acl.h" +#include "acl/ops/acl_dvpp.h" +#include "atlas_utils_common.h" + +using namespace std; + +extern "C" { + +#if 0 +void* CopyDataHostToDvpp(void* data, int size) { + void* buffer = nullptr; + + auto aclRet = acldvppMalloc(&buffer, size); + if (aclRet != ACL_ERROR_NONE) { + ASC_LOG_ERROR("acl malloc dvpp data failed, dataSize=%u, ret=%d", + size, aclRet); + return nullptr; + } + printf("malloc dvpp memory size %d ok", size); + // copy input to device memory + aclRet = aclrtMemcpy(buffer, size, data, size, ACL_MEMCPY_HOST_TO_DEVICE); + if (aclRet != ACL_ERROR_NONE) { + ASC_LOG_ERROR("acl memcpy data to dvpp failed, size %u, error %d", size, aclRet); + acldvppFree(buffer); + return nullptr; + } + printf("copy data to dvpp ok"); + + return buffer; +} + +void* CopyDataHostToDevice(void* data, int size) { + void* buffer = nullptr; + + auto aclRet = aclrtMalloc(&buffer, size, ACL_MEM_MALLOC_NORMAL_ONLY); + if (aclRet != ACL_ERROR_NONE) { + ASC_LOG_ERROR("acl malloc device memory failed, dataSize=%u, ret=%d", + size, aclRet); + return nullptr; + } + // copy input to device memory + aclRet = aclrtMemcpy(buffer, size, data, size, ACL_MEMCPY_HOST_TO_DEVICE); + if (aclRet != ACL_ERROR_NONE) { + ASC_LOG_ERROR("acl memcpy data to dev failed, size %u, error %d", size, aclRet); + aclrtFree(buffer); + return nullptr; + } + + return buffer; +} + +void* CopyDataDeviceToDevice(void* data, int size) { + void* buffer = nullptr; + + auto aclRet = aclrtMalloc(&buffer, size, ACL_MEM_MALLOC_NORMAL_ONLY); + if (aclRet != ACL_ERROR_NONE) { + ASC_LOG_ERROR("acl malloc device memory failed, dataSize=%u, ret=%d", + size, aclRet); + return nullptr; + } + // copy input to device memory + aclRet = aclrtMemcpy(buffer, size, data, size, ACL_MEMCPY_DEVICE_TO_DEVICE); + if (aclRet != ACL_ERROR_NONE) { + ASC_LOG_ERROR("acl memcpy data to dev failed, size %u, error %d", size, aclRet); + aclrtFree(buffer); + return nullptr; + } + + return buffer; +} + +void* CopyDataDeviceToHost(void* deviceData, uint32_t dataLen) { + void *outHostData = nullptr; + + aclError ret = aclrtMallocHost(&outHostData, dataLen); + if (ret != ACL_ERROR_NONE) { + ASC_LOG_ERROR("aclrtMallocHost failed, ret[%d]", ret); + return nullptr; + } + + ret = aclrtMemcpy(outHostData, dataLen, deviceData, + dataLen, ACL_MEMCPY_DEVICE_TO_HOST); + if (ret != ACL_ERROR_NONE) { + ASC_LOG_ERROR("aclrtMemcpy failed, ret[%d]", ret); + aclrtFreeHost(outHostData); + return nullptr; + } + + return outHostData; +} + +void* CopyDataDeviceToNewBuf(void* deviceData, uint32_t dataLen) { + uint8_t* outHostData = new uint8_t[dataLen]; + +/* aclError ret = aclrtMallocHost(&outHostData, dataLen); + if (ret != ACL_ERROR_NONE) { + ASC_LOG_ERROR("aclrtMallocHost failed, ret[%d]", ret); + return nullptr; + } +*/ + int ret = aclrtMemcpy(outHostData, dataLen, deviceData, + dataLen, ACL_MEMCPY_DEVICE_TO_HOST); + if (ret != ACL_ERROR_NONE) { + ASC_LOG_ERROR("aclrtMemcpy failed, ret[%d]", ret); + aclrtFreeHost(outHostData); + return nullptr; + } + + return (void *)outHostData; +} + + + +void SaveBinFile(const char* filename, void* data, uint32_t size) { + FILE *outFileFp = fopen(filename, "wb+"); + if (outFileFp == nullptr) { + ASC_LOG_ERROR("Save file %s failed for open error", filename); + return; + } + fwrite(data, 1, size, outFileFp); + + fflush(outFileFp); + fclose(outFileFp); +} + +char* ReadBinFile(const std::string& fileName, uint32_t& fileSize) +{ + std::ifstream binFile(fileName, std::ifstream::binary); + if (binFile.is_open() == false) { + ASC_LOG_ERROR("open file %s failed", fileName.c_str()); + return nullptr; + } + + binFile.seekg(0, binFile.end); + uint32_t binFileBufferLen = binFile.tellg(); + if (binFileBufferLen == 0) { + ASC_LOG_ERROR("binfile is empty, filename is %s", fileName.c_str()); + binFile.close(); + return nullptr; + } + + binFile.seekg(0, binFile.beg); + + char* binFileBufferData = new(std::nothrow) char[binFileBufferLen]; + if (binFileBufferData == nullptr) { + ASC_LOG_ERROR("malloc binFileBufferData failed"); + binFile.close(); + return nullptr; + } + binFile.read(binFileBufferData, binFileBufferLen); + binFile.close(); + fileSize = binFileBufferLen; + return binFileBufferData; +} + +int ReadImageFile(ImageData* image, const string& filename) { + char* data; + uint32_t size = 0; + + data = ReadBinFile(filename, size); + if (data == nullptr) { + ASC_LOG_ERROR("Read image file %s failed", filename.c_str()); + return STATUS_ERROR; + } + + image->data = SHARED_PRT_U8_BUF(data); + image->size = size; + printf("read image ok, size %d", size); + return STATUS_OK; +} +#endif +} diff --git a/atlas_utils/lib/src/atlas_utils_common.h b/atlas_utils/lib/src/atlas_utils_common.h new file mode 100644 index 0000000..3207b28 --- /dev/null +++ b/atlas_utils/lib/src/atlas_utils_common.h @@ -0,0 +1,130 @@ +/** + * ============================================================================ + * + * Copyright (C) 2018, Hisilicon Technologies Co., Ltd. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1 Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2 Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3 Neither the names of the copyright holders nor the names of the + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + */ + +#ifndef _ATLAS_UTILS_COMMON_H_ +#define _ATLAS_UTILS_COMMON_H_ +#include +#include +#include +#include + +#include "acl/acl_base.h" + +using namespace std; + +#define STATUS_ERROR -1 +#define STATUS_OK 0 + +#define ALIGN_UP(num, align) (((num) + (align) - 1) & ~((align) - 1)) +#define ALIGN_UP2(num) ALIGN_UP(num, 2) +#define ALIGN_UP16(num) ALIGN_UP(num, 16) +#define ALIGN_UP128(num) ALIGN_UP(num, 128) + +#define YUV420SP_SIZE(width, height) ((width) * (height) * 3 / 2) +//#define SHARED_PRT_DVPP_BUF(buf) (shared_ptr((uint8_t *)(buf), [](uint8_t* p) { acldvppFree(p); })) +//#define SHARED_PRT_U8_BUF(buf) (shared_ptr((uint8_t *)(buf), [](uint8_t* p) { delete[](p); })) + + +#define ASC_LOG_ERROR(fmt, ...) \ + do{aclAppLog(ACL_ERROR, __FUNCTION__, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \ + printf(fmt"\n", ##__VA_ARGS__);}while(0) + +#define ASC_LOG_INFO(fmt, ...) \ + do{aclAppLog(ACL_INFO, __FUNCTION__, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \ + printf(fmt"\n", ##__VA_ARGS__);}while(0) + +#define ASC_LOG_DEBUG(fmt, ...) \ + do{aclAppLog(ACL_DEBUG, __FUNCTION__, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \ + printf(fmt"\n", ##__VA_ARGS__);}while(0) +#if 0 +struct ImageData { + bool isAligned = false; + uint32_t format = 1; //PIXEL_FORMAT_YUV_SEMIPLANAR_420; + uint32_t width = 0; + uint32_t height = 0; + uint32_t alignWidth = 0; + uint32_t alignHeight = 0; + uint32_t size = 0; + std::shared_ptr data; +}; + +struct FrameData { + bool isFinished = false; + uint32_t width = 0; + uint32_t height = 0; + uint32_t frameId = 0; + ImageData image; +}; + +struct Resolution { + uint32_t width; + uint32_t height; +}; + +struct BoxArea { + uint32_t ltx; + uint32_t lty; + uint32_t width; + uint32_t height; +}; + + +struct DataBuffer { + uint32_t size; + std::shared_ptr data; +}; + +struct DetectionData { + ImageData image; + std::vector output; +}; + +struct AtlasMessage { + int dest; + int msgId; + std::shared_ptr data; +}; + +extern "C" { +void* CopyDataDeviceToHost(void* deviceData, uint32_t dataLen); +void* CopyDataHostToDvpp(void* data, int size); +void* CopyDataHostToDevice(void* data, int size); +void* CopyDataDeviceToDevice(void* data, int size); +void* CopyDataDeviceToNewBuf(void* deviceData, uint32_t dataLen); + +void SaveBinFile(const char* filename, void* data, uint32_t size); +int ReadImageFile(ImageData* image, const string& filename); + +} +#endif +#endif /* HIAI_APP_IMAGE_POOL_H_ */ diff --git a/atlas_utils/lib/src/camera.cpp b/atlas_utils/lib/src/camera.cpp new file mode 100644 index 0000000..5a789e0 --- /dev/null +++ b/atlas_utils/lib/src/camera.cpp @@ -0,0 +1,164 @@ +/** + * ============================================================================ + * + * Copyright (C) 2018, Hisilicon Technologies Co., Ltd. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1 Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2 Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3 Neither the names of the copyright holders nor the names of the + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "acl/acl.h" +#include "acl/ops/acl_dvpp.h" +#include "atlas_utils_common.h" +#include "camera.h" + +using namespace std; + +extern "C" { +#include "peripheral_api.h" +#include "camera.h" + +CameraManager g_CameraMgr; + +int CameraInit(int id, int fps, int width, int height) { + if (!g_CameraMgr.hwInited) { + MediaLibInit(); + g_CameraMgr.hwInited = 1; + } + + Camera& cap = CAMERA(id); + cap.frameSize = YUV420SP_SIZE(width, height); + cap.id = id; + cap.fps = fps; + cap.width = width; + cap.height = height; + cap.inited = true; + + return STATUS_OK; +} + +int ConfigCamera(int id, int fps, int width, int height) { + int ret = SetCameraProperty(id, CAMERA_PROP_FPS, &fps); + if (ret == LIBMEDIA_STATUS_FAILED) { + ASC_LOG_ERROR("Set camera fps failed"); + return STATUS_ERROR; + } + + CameraResolution resolution; + resolution.width = width; + resolution.height = height; + ret = SetCameraProperty(id, CAMERA_PROP_RESOLUTION, &resolution); + if (ret == LIBMEDIA_STATUS_FAILED) { + ASC_LOG_ERROR("Set camera resolution failed"); + return STATUS_ERROR; + } + + CameraCapMode mode = CAMERA_CAP_ACTIVE; + ret = SetCameraProperty(id, CAMERA_PROP_CAP_MODE, &mode); + if (ret == LIBMEDIA_STATUS_FAILED) { + ASC_LOG_ERROR("Set camera mode:%d failed", mode); + return STATUS_ERROR; + } + + return STATUS_OK; +} + +int OpenCameraEx(int id, int fps, int width, int height) { + if ((id < 0) || (id >= CAMERA_NUM)) { + ASC_LOG_ERROR("Open camera failed for invalid id %d", id); + return STATUS_ERROR; + } + + if (!CAMERA(id).inited) { + CameraInit(id, fps, width, height); + } + + CameraStatus status = QueryCameraStatus(id); + if (status == CAMERA_STATUS_CLOSED){ + // Open Camera + if (LIBMEDIA_STATUS_FAILED == OpenCamera(id)) { + ASC_LOG_ERROR("Camera%d closed, and open failed.", id); + return STATUS_ERROR; + } + } else if (status != CAMERA_STATUS_OPEN) { + ASC_LOG_ERROR("Invalid camera%d status %d", id, status); + return STATUS_ERROR; + } + + //Set camera property + if (STATUS_OK != ConfigCamera(id, fps, width, height)) { + CloseCamera(id); + ASC_LOG_ERROR("Set camera%d property failed", id); + return STATUS_ERROR; + } + + ASC_LOG_INFO("Open camera %d success", id); + + return STATUS_OK; +} + +int ReadCameraFrame(int id, CameraOutput& frame) { + int size = CAMERA(id).frameSize; + void* data = nullptr; + auto aclRet = acldvppMalloc(&data, size); + if (aclRet != ACL_ERROR_NONE) { + ASC_LOG_ERROR("acl malloc dvpp data failed, dataSize=%u, ret=%d", + size, aclRet); + return STATUS_ERROR; + } + + int ret = ReadFrameFromCamera(id, (void*)data, (int *)&size); + if ((ret == LIBMEDIA_STATUS_FAILED) || + (size != CAMERA(id).frameSize)) { + acldvppFree(data); + ASC_LOG_ERROR("Get image from camera %d failed, size %d", id, size); + return STATUS_ERROR; + } + frame.size = size; + frame.data = (uint8_t*)data; + ASC_LOG_INFO("cpp image ptr 0x%x", data); + return STATUS_OK; +} + +int CloseCameraEx(int cameraId) { + if (LIBMEDIA_STATUS_FAILED == CloseCamera(cameraId)) { + ASC_LOG_ERROR("Close camera %d failed", cameraId); + return STATUS_ERROR; + } + + return STATUS_OK; +} + + +} diff --git a/atlas_utils/lib/src/camera.h b/atlas_utils/lib/src/camera.h new file mode 100644 index 0000000..6d6dd2e --- /dev/null +++ b/atlas_utils/lib/src/camera.h @@ -0,0 +1,63 @@ +/** + * ============================================================================ + * + * Copyright (C) 2018, Hisilicon Technologies Co., Ltd. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1 Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2 Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3 Neither the names of the copyright holders nor the names of the + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + */ +#ifndef _CAMERA_H +#define _CAMERA_H + +#define CAMERA_NUM (2) + +#define CAMERA(i) (g_CameraMgr.cap[i]) + +struct CameraOutput { + int size; + uint8_t* data; +}; + +struct Camera { + bool inited = false; + int id = 255; + int fps = 0; + int width = 0; + int height = 0; + int frameSize = 0; + +}; + +struct CameraManager { + bool hwInited = 0; + Camera cap[CAMERA_NUM]; +}; + + + + +#endif diff --git a/atlas_utils/lib/src/out/libatlasutil.so b/atlas_utils/lib/src/out/libatlasutil.so new file mode 100644 index 0000000..97ea0f1 Binary files /dev/null and b/atlas_utils/lib/src/out/libatlasutil.so differ diff --git a/atlas_utils/lib/src/out/obj/atlas_utils_common.o b/atlas_utils/lib/src/out/obj/atlas_utils_common.o new file mode 100644 index 0000000..6318a83 Binary files /dev/null and b/atlas_utils/lib/src/out/obj/atlas_utils_common.o differ diff --git a/atlas_utils/lib/src/out/obj/camera.o b/atlas_utils/lib/src/out/obj/camera.o new file mode 100644 index 0000000..33e82ce Binary files /dev/null and b/atlas_utils/lib/src/out/obj/camera.o differ diff --git a/atlas_utils/presenteragent/__init__.py b/atlas_utils/presenteragent/__init__.py new file mode 100644 index 0000000..d9a9180 --- /dev/null +++ b/atlas_utils/presenteragent/__init__.py @@ -0,0 +1,6 @@ +from .presenter_datatype import * +from .presenter_agent import * +from .presenter_channel import * +#from .presenter_message_pb2 import * + +__all__ = ['presenter_datatype.py', 'presenter_agent', 'presenter_channel', 'presenter_message_pb'] diff --git a/atlas_utils/presenteragent/presenter_agent.py b/atlas_utils/presenteragent/presenter_agent.py new file mode 100644 index 0000000..47daf7c --- /dev/null +++ b/atlas_utils/presenteragent/presenter_agent.py @@ -0,0 +1,75 @@ +# !/usr/bin/env python +# -*- coding:utf-8 -*- +import time +from threading import Thread + +from .socket_client import AgentSocket +from . import presenter_message as pm +from . import presenter_datatype as datatype + + +class PresenterAgent(): + def __init__(self, server_ip, port): + self.socket = AgentSocket(server_ip, port) + self._closed = False + + def connect_server(self): + return self.socket.connect() + + def start_heard_beat_thread(self): + self.heart_beat_thread = Thread(target=self._keep_alive) + self.heart_beat_thread.start() + + def _keep_alive(self): + msg = pm.heartbeat_message() + + while True: + if self._closed: + print("ERROR:Heard beat thread exit") + break + + self.socket.send_msg(msg) + time.sleep(2) + + def exit(self): + self.socket.close() + self._closed = True + + +def StartPresenterAgent(msg_queue, server_ip, port, open_status, data_respone_counter): + agent = PresenterAgent(server_ip, port) + ret = agent.connect_server() + if ret: + print("ERROR:Connect server failed, ret =", ret) + return + + open_status.value = datatype.STATUS_CONNECTED + + while True: + data = msg_queue.get() + + if open_status.value == datatype.STATUS_EXITING: + open_status.value = datatype.STATUS_EXITTED + agent.exit() + break + + if data: + agent.socket.send_msg(data) + + msg_name, msg_body = agent.socket.recv_msg() + if (msg_name == None) or (msg_body == None): + print("ERROR:Recv invalid message, message name ", msg_name) + continue + + if ((open_status.value == datatype.STATUS_CONNECTED) + and pm.is_open_channel_response(msg_name)): + print("Received open channel respone") + open_status.value = datatype.STATUS_OPENED + agent.start_heard_beat_thread() + print("presenter agent change connect_status to ", open_status.value) + + if ((open_status.value == datatype.STATUS_OPENED) and + pm.is_image_frame_response(msg_name)): + data_respone_counter.value += 1 + print("send ok ", data_respone_counter.value) + diff --git a/atlas_utils/presenteragent/presenter_channel.py b/atlas_utils/presenteragent/presenter_channel.py new file mode 100644 index 0000000..d1e35e0 --- /dev/null +++ b/atlas_utils/presenteragent/presenter_channel.py @@ -0,0 +1,159 @@ +# !/usr/bin/env python +# -*- coding:utf-8 -*- +import ctypes +from ctypes import * +import time +import configparser +from multiprocessing import Process, Queue, Manager +import queue +import numpy as np +import sys +sys.path.append('..') + +import acl +from ..constants import * +from ..lib.atlasutil_so import libatlas + +from .presenter_datatype import * +from . import presenter_agent as agent +from . import presenter_message as pm + + +class DataBufC(Structure): + _fields_ = [ + ('size', c_int), + ('data', POINTER(c_ubyte)) + ] + +class DataBuf(): + def __init__(self, data, data_size): + self.data = data + self.size = data_size + self.nparray = None + + def copy_to_local(self): + src_data = DataBufC() + src_data.data = cast(self.data, POINTER(c_ubyte)) + src_data.size = self.size + dest_data = DataBufC() + ret = libatlas.CopyDataToLocal(byref(dest_data), byref(src_data)) + if ret: + print("Copy data to local failed") + return None + + return DataBuf(dest_data.data, dest_data.size) + + def tobytes(self): + self.nparray = np.frombuffer((ctypes.c_ubyte * self.size).from_address(ctypes.addressof(self.data.contents)), dtype=np.uint8) + return self.nparray.tobytes() + + def destroy(self): + data_buf = DataBufC() + data_buf.data = cast(self.data, POINTER(c_ubyte)) + data_buf.size = self.size + libatlas.ReleaseDataBuf(byref(data_buf)) + self.data = None + self.size = 0 + + +class PresenterChannel(): + def __init__(self, server_ip, port, name='video', type=CONTENT_TYPE_VIDEO): + self._server_ip = server_ip + self._port = port + self._type = type + self._name = name + self.agent_msg_queue = Queue() + self.open_status = Manager().Value('i', STATUS_DISCONNECT) + self.data_respone_counter = Manager().Value('i', 0) + self._send_counter = 0 + self._send_buffer = queue.Queue(64) + self.send_cnt = 0 + self.relase_cnt = 0 + + def startup(self): + agent_process = Process(target=agent.StartPresenterAgent, + args=(self.agent_msg_queue, self._server_ip,self._port, + self.open_status, self.data_respone_counter)) + agent_process.start() + time.sleep(0.5) + + self._send_open_channel_request(self._name, self._type) + + return self._wait_open_status(STATUS_OPENED) + + def _wait_open_status(self, listen_status): + ret = STATUS_ERROR + for i in range(0, 100): + time.sleep(0.1) + if self.open_status.value == listen_status: + print("Open status is %d now"%(listen_status)) + ret = STATUS_OK + break + + return ret + + def send_message(self, data): + self.agent_msg_queue.put(data) + self._send_counter += 1 + + def _send_open_channel_request(self, channel_name, content_type): + request_msg = pm.open_channel_request(channel_name, content_type) + self.send_message(request_msg) + + def _release_send_success_data(self): + release_num = self._send_buffer.qsize() - \ + (self._send_counter - self.data_respone_counter.value) + if release_num > 0: + for i in range(0, release_num): + data = self._send_buffer.get_nowait() + data.destroy() + data = None + self.relase_cnt += 1 + #print("Released send success images ", self.relase_cnt) + + def send_detection_data(self, image_width, image_height, + image_data, detection_result): + if self._send_buffer.full() is True: + print("ERROR:Send detection data failed for buffer is full") + return False + + image_buf = DataBuf(image_data.data(), image_data.size).copy_to_local() + request_msg = pm.image_frame_request(image_width, image_height, + image_buf.tobytes(), detection_result) + self.send_message(request_msg) + self._send_buffer.put(image_buf) + self._release_send_success_data() + + return True + + def _send_heart_beat_message(self): + msg = pm.heartbeat_message() + self.send_message(msg) + + def __del__(self): + self.open_status.value = STATUS_EXITING + print("Presenter channel close...") + self._send_heart_beat_message() + if STATUS_OK == self._wait_open_status(STATUS_EXITTED): + print("Presenter channel closed") + else: + print("Presenter channel close failed for presenter agent no response") + + +def get_presenter_server_addr(config_file): + config = configparser.ConfigParser() + config.read(config_file) + presenter_server_ip = config['baseconf']['presenter_server_ip'] + port = int(config['baseconf']['presenter_server_port']) + + print("presenter server ip %s, port %d"%(presenter_server_ip, port)) + return presenter_server_ip, port + +def open_channel(config_file, channel_name='video', channel_type = CONTENT_TYPE_VIDEO): + server_ip, port = get_presenter_server_addr(config_file) + channel = PresenterChannel(server_ip, port, channel_name, channel_type) + ret = channel.startup() + if ret: + print("ERROR:Open channel failed") + return None + return channel diff --git a/atlas_utils/presenteragent/presenter_datatype.py b/atlas_utils/presenteragent/presenter_datatype.py new file mode 100644 index 0000000..7cb353d --- /dev/null +++ b/atlas_utils/presenteragent/presenter_datatype.py @@ -0,0 +1,38 @@ + +STATUS_DISCONNECT = 0 +STATUS_CONNECTED = 1 +STATUS_OPEN_CH_REQUEST = 2 +STATUS_OPENED = 3 +STATUS_EXITING = 4 +STATUS_EXITTED = 5 + +CONTENT_TYPE_IMAGE = 0 +CONTENT_TYPE_VIDEO = 1 + +STATUS_OK = 0 +STATUS_ERROR = 1 + + +class Point(): + def __init__(self, x=0, y=0): + self.x = x + self.y = y + +class Box(): + def __init__(self, lt, rb): + self.lt = Point(lt) + self.rb = Point(rb) + + def box_valid(self): + return ((self.lt.x >= 0) + and (self.lt.y >= 0) + and (self.rb.x >= self.lt.x) + and (self.rb.y >= self.lt.y)) + +class ObjectDetectionResult(): + def __init__(self, ltx=0, lty=0, rbx=0, rby=0, text=None): + self.object_class = 0 + self.confidence = 0 + self.box = Box((ltx, lty), (rbx, rby)) + self.result_text = text + diff --git a/atlas_utils/presenteragent/presenter_message.proto b/atlas_utils/presenteragent/presenter_message.proto new file mode 100644 index 0000000..879d557 --- /dev/null +++ b/atlas_utils/presenteragent/presenter_message.proto @@ -0,0 +1,67 @@ +syntax = "proto3"; + +package ascend.presenter.proto; + +enum OpenChannelErrorCode { + kOpenChannelErrorNone = 0; + kOpenChannelErrorNoSuchChannel = 1; + kOpenChannelErrorChannelAlreadyOpened = 2; + kOpenChannelErrorOther = -1; +} + +enum ChannelContentType { + kChannelContentTypeImage = 0; + kChannelContentTypeVideo = 1; +} + +// By Protocol Buffer Style Guide, need to use underscore_separated_names +// for field names +message OpenChannelRequest { + string channel_name = 1; + ChannelContentType content_type = 2; +} + +message OpenChannelResponse { + OpenChannelErrorCode error_code = 1; + string error_message = 2; +} + +message HeartbeatMessage { + +} + +enum ImageFormat { + kImageFormatJpeg = 0; +} + +message Coordinate { + uint32 x = 1; + uint32 y = 2; +} + +message Rectangle_Attr { + Coordinate left_top = 1; + Coordinate right_bottom = 2; + string label_text = 3; +} + +message PresentImageRequest { + ImageFormat format = 1; + uint32 width = 2; + uint32 height = 3; + bytes data = 4; + repeated Rectangle_Attr rectangle_list = 5; +} + +enum PresentDataErrorCode { + kPresentDataErrorNone = 0; + kPresentDataErrorUnsupportedType = 1; + kPresentDataErrorUnsupportedFormat = 2; + kPresentDataErrorOther = -1; +} + +message PresentImageResponse { + PresentDataErrorCode error_code = 1; + string error_message = 2; +} + diff --git a/atlas_utils/presenteragent/presenter_message.py b/atlas_utils/presenteragent/presenter_message.py new file mode 100644 index 0000000..b98d102 --- /dev/null +++ b/atlas_utils/presenteragent/presenter_message.py @@ -0,0 +1,56 @@ +# !/usr/bin/env python +# -*- coding:utf-8 -*- +import struct +import socket + +from . import presenter_message_pb2 as pb2 + +def pack_message(msg_name, msg_data): + buf = msg_data.SerializeToString() + msg_body_len = len(buf) + msg_name_len = len(msg_name) + msg_total_len = msg_name_len + msg_body_len + 5 + data = b'' + msg_total_len = socket.htonl(msg_total_len) + pack_data = struct.pack('IB', msg_total_len, msg_name_len) + data += pack_data + data += msg_name.encode() + data += buf + + return data + +def open_channel_request(channel_name, content_type): + request = pb2.OpenChannelRequest() + request.channel_name = channel_name + request.content_type = content_type + + return pack_message(pb2._OPENCHANNELREQUEST.full_name, request) + +def image_frame_request(image_width, image_height, image_data, detection_result): + request = pb2.PresentImageRequest() + request.format = 0 + request.width = image_width + request.height = image_height + request.data = image_data + for i in range(0, len(detection_result)): + myadd = request.rectangle_list.add() + myadd.left_top.x = detection_result[i].box.lt.x + myadd.left_top.y = detection_result[i].box.lt.y + myadd.right_bottom.x = detection_result[i].box.rb.x + myadd.right_bottom.y = detection_result[i].box.rb.y + myadd.label_text = detection_result[i].result_text + + return pack_message(pb2._PRESENTIMAGEREQUEST.full_name, request) + +def heartbeat_message(): + return pack_message(pb2._HEARTBEATMESSAGE.full_name, pb2.HeartbeatMessage()) + +def is_open_channel_response(msg_name): + return (msg_name == pb2._OPENCHANNELRESPONSE.full_name) + +def is_image_frame_response(msg_name): + return (msg_name == pb2._PRESENTIMAGERESPONSE.full_name) + + + + diff --git a/atlas_utils/presenteragent/presenter_message_pb2.py b/atlas_utils/presenteragent/presenter_message_pb2.py new file mode 100644 index 0000000..6c99f06 --- /dev/null +++ b/atlas_utils/presenteragent/presenter_message_pb2.py @@ -0,0 +1,493 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: presenter_message.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='presenter_message.proto', + package='ascend.presenter.proto', + syntax='proto3', + serialized_pb=_b('\n\x17presenter_message.proto\x12\x16\x61scend.presenter.proto\"l\n\x12OpenChannelRequest\x12\x14\n\x0c\x63hannel_name\x18\x01 \x01(\t\x12@\n\x0c\x63ontent_type\x18\x02 \x01(\x0e\x32*.ascend.presenter.proto.ChannelContentType\"n\n\x13OpenChannelResponse\x12@\n\nerror_code\x18\x01 \x01(\x0e\x32,.ascend.presenter.proto.OpenChannelErrorCode\x12\x15\n\rerror_message\x18\x02 \x01(\t\"\x12\n\x10HeartbeatMessage\"\"\n\nCoordinate\x12\t\n\x01x\x18\x01 \x01(\r\x12\t\n\x01y\x18\x02 \x01(\r\"\x94\x01\n\x0eRectangle_Attr\x12\x34\n\x08left_top\x18\x01 \x01(\x0b\x32\".ascend.presenter.proto.Coordinate\x12\x38\n\x0cright_bottom\x18\x02 \x01(\x0b\x32\".ascend.presenter.proto.Coordinate\x12\x12\n\nlabel_text\x18\x03 \x01(\t\"\xb7\x01\n\x13PresentImageRequest\x12\x33\n\x06\x66ormat\x18\x01 \x01(\x0e\x32#.ascend.presenter.proto.ImageFormat\x12\r\n\x05width\x18\x02 \x01(\r\x12\x0e\n\x06height\x18\x03 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12>\n\x0erectangle_list\x18\x05 \x03(\x0b\x32&.ascend.presenter.proto.Rectangle_Attr\"o\n\x14PresentImageResponse\x12@\n\nerror_code\x18\x01 \x01(\x0e\x32,.ascend.presenter.proto.PresentDataErrorCode\x12\x15\n\rerror_message\x18\x02 \x01(\t*\xa5\x01\n\x14OpenChannelErrorCode\x12\x19\n\x15kOpenChannelErrorNone\x10\x00\x12\"\n\x1ekOpenChannelErrorNoSuchChannel\x10\x01\x12)\n%kOpenChannelErrorChannelAlreadyOpened\x10\x02\x12#\n\x16kOpenChannelErrorOther\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01*P\n\x12\x43hannelContentType\x12\x1c\n\x18kChannelContentTypeImage\x10\x00\x12\x1c\n\x18kChannelContentTypeVideo\x10\x01*#\n\x0bImageFormat\x12\x14\n\x10kImageFormatJpeg\x10\x00*\xa4\x01\n\x14PresentDataErrorCode\x12\x19\n\x15kPresentDataErrorNone\x10\x00\x12$\n kPresentDataErrorUnsupportedType\x10\x01\x12&\n\"kPresentDataErrorUnsupportedFormat\x10\x02\x12#\n\x16kPresentDataErrorOther\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x62\x06proto3') +) + +_OPENCHANNELERRORCODE = _descriptor.EnumDescriptor( + name='OpenChannelErrorCode', + full_name='ascend.presenter.proto.OpenChannelErrorCode', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='kOpenChannelErrorNone', index=0, number=0, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='kOpenChannelErrorNoSuchChannel', index=1, number=1, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='kOpenChannelErrorChannelAlreadyOpened', index=2, number=2, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='kOpenChannelErrorOther', index=3, number=-1, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=780, + serialized_end=945, +) +_sym_db.RegisterEnumDescriptor(_OPENCHANNELERRORCODE) + +OpenChannelErrorCode = enum_type_wrapper.EnumTypeWrapper(_OPENCHANNELERRORCODE) +_CHANNELCONTENTTYPE = _descriptor.EnumDescriptor( + name='ChannelContentType', + full_name='ascend.presenter.proto.ChannelContentType', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='kChannelContentTypeImage', index=0, number=0, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='kChannelContentTypeVideo', index=1, number=1, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=947, + serialized_end=1027, +) +_sym_db.RegisterEnumDescriptor(_CHANNELCONTENTTYPE) + +ChannelContentType = enum_type_wrapper.EnumTypeWrapper(_CHANNELCONTENTTYPE) +_IMAGEFORMAT = _descriptor.EnumDescriptor( + name='ImageFormat', + full_name='ascend.presenter.proto.ImageFormat', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='kImageFormatJpeg', index=0, number=0, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=1029, + serialized_end=1064, +) +_sym_db.RegisterEnumDescriptor(_IMAGEFORMAT) + +ImageFormat = enum_type_wrapper.EnumTypeWrapper(_IMAGEFORMAT) +_PRESENTDATAERRORCODE = _descriptor.EnumDescriptor( + name='PresentDataErrorCode', + full_name='ascend.presenter.proto.PresentDataErrorCode', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='kPresentDataErrorNone', index=0, number=0, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='kPresentDataErrorUnsupportedType', index=1, number=1, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='kPresentDataErrorUnsupportedFormat', index=2, number=2, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='kPresentDataErrorOther', index=3, number=-1, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=1067, + serialized_end=1231, +) +_sym_db.RegisterEnumDescriptor(_PRESENTDATAERRORCODE) + +PresentDataErrorCode = enum_type_wrapper.EnumTypeWrapper(_PRESENTDATAERRORCODE) +kOpenChannelErrorNone = 0 +kOpenChannelErrorNoSuchChannel = 1 +kOpenChannelErrorChannelAlreadyOpened = 2 +kOpenChannelErrorOther = -1 +kChannelContentTypeImage = 0 +kChannelContentTypeVideo = 1 +kImageFormatJpeg = 0 +kPresentDataErrorNone = 0 +kPresentDataErrorUnsupportedType = 1 +kPresentDataErrorUnsupportedFormat = 2 +kPresentDataErrorOther = -1 + + + +_OPENCHANNELREQUEST = _descriptor.Descriptor( + name='OpenChannelRequest', + full_name='ascend.presenter.proto.OpenChannelRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='channel_name', full_name='ascend.presenter.proto.OpenChannelRequest.channel_name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='content_type', full_name='ascend.presenter.proto.OpenChannelRequest.content_type', index=1, + number=2, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=51, + serialized_end=159, +) + + +_OPENCHANNELRESPONSE = _descriptor.Descriptor( + name='OpenChannelResponse', + full_name='ascend.presenter.proto.OpenChannelResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='error_code', full_name='ascend.presenter.proto.OpenChannelResponse.error_code', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='error_message', full_name='ascend.presenter.proto.OpenChannelResponse.error_message', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=161, + serialized_end=271, +) + + +_HEARTBEATMESSAGE = _descriptor.Descriptor( + name='HeartbeatMessage', + full_name='ascend.presenter.proto.HeartbeatMessage', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=273, + serialized_end=291, +) + + +_COORDINATE = _descriptor.Descriptor( + name='Coordinate', + full_name='ascend.presenter.proto.Coordinate', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='x', full_name='ascend.presenter.proto.Coordinate.x', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='y', full_name='ascend.presenter.proto.Coordinate.y', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=293, + serialized_end=327, +) + + +_RECTANGLE_ATTR = _descriptor.Descriptor( + name='Rectangle_Attr', + full_name='ascend.presenter.proto.Rectangle_Attr', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='left_top', full_name='ascend.presenter.proto.Rectangle_Attr.left_top', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='right_bottom', full_name='ascend.presenter.proto.Rectangle_Attr.right_bottom', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='label_text', full_name='ascend.presenter.proto.Rectangle_Attr.label_text', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=330, + serialized_end=478, +) + + +_PRESENTIMAGEREQUEST = _descriptor.Descriptor( + name='PresentImageRequest', + full_name='ascend.presenter.proto.PresentImageRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='format', full_name='ascend.presenter.proto.PresentImageRequest.format', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='width', full_name='ascend.presenter.proto.PresentImageRequest.width', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='height', full_name='ascend.presenter.proto.PresentImageRequest.height', index=2, + number=3, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='data', full_name='ascend.presenter.proto.PresentImageRequest.data', index=3, + number=4, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='rectangle_list', full_name='ascend.presenter.proto.PresentImageRequest.rectangle_list', index=4, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=481, + serialized_end=664, +) + + +_PRESENTIMAGERESPONSE = _descriptor.Descriptor( + name='PresentImageResponse', + full_name='ascend.presenter.proto.PresentImageResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='error_code', full_name='ascend.presenter.proto.PresentImageResponse.error_code', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='error_message', full_name='ascend.presenter.proto.PresentImageResponse.error_message', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=666, + serialized_end=777, +) + +_OPENCHANNELREQUEST.fields_by_name['content_type'].enum_type = _CHANNELCONTENTTYPE +_OPENCHANNELRESPONSE.fields_by_name['error_code'].enum_type = _OPENCHANNELERRORCODE +_RECTANGLE_ATTR.fields_by_name['left_top'].message_type = _COORDINATE +_RECTANGLE_ATTR.fields_by_name['right_bottom'].message_type = _COORDINATE +_PRESENTIMAGEREQUEST.fields_by_name['format'].enum_type = _IMAGEFORMAT +_PRESENTIMAGEREQUEST.fields_by_name['rectangle_list'].message_type = _RECTANGLE_ATTR +_PRESENTIMAGERESPONSE.fields_by_name['error_code'].enum_type = _PRESENTDATAERRORCODE +DESCRIPTOR.message_types_by_name['OpenChannelRequest'] = _OPENCHANNELREQUEST +DESCRIPTOR.message_types_by_name['OpenChannelResponse'] = _OPENCHANNELRESPONSE +DESCRIPTOR.message_types_by_name['HeartbeatMessage'] = _HEARTBEATMESSAGE +DESCRIPTOR.message_types_by_name['Coordinate'] = _COORDINATE +DESCRIPTOR.message_types_by_name['Rectangle_Attr'] = _RECTANGLE_ATTR +DESCRIPTOR.message_types_by_name['PresentImageRequest'] = _PRESENTIMAGEREQUEST +DESCRIPTOR.message_types_by_name['PresentImageResponse'] = _PRESENTIMAGERESPONSE +DESCRIPTOR.enum_types_by_name['OpenChannelErrorCode'] = _OPENCHANNELERRORCODE +DESCRIPTOR.enum_types_by_name['ChannelContentType'] = _CHANNELCONTENTTYPE +DESCRIPTOR.enum_types_by_name['ImageFormat'] = _IMAGEFORMAT +DESCRIPTOR.enum_types_by_name['PresentDataErrorCode'] = _PRESENTDATAERRORCODE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +OpenChannelRequest = _reflection.GeneratedProtocolMessageType('OpenChannelRequest', (_message.Message,), dict( + DESCRIPTOR = _OPENCHANNELREQUEST, + __module__ = 'presenter_message_pb2' + # @@protoc_insertion_point(class_scope:ascend.presenter.proto.OpenChannelRequest) + )) +_sym_db.RegisterMessage(OpenChannelRequest) + +OpenChannelResponse = _reflection.GeneratedProtocolMessageType('OpenChannelResponse', (_message.Message,), dict( + DESCRIPTOR = _OPENCHANNELRESPONSE, + __module__ = 'presenter_message_pb2' + # @@protoc_insertion_point(class_scope:ascend.presenter.proto.OpenChannelResponse) + )) +_sym_db.RegisterMessage(OpenChannelResponse) + +HeartbeatMessage = _reflection.GeneratedProtocolMessageType('HeartbeatMessage', (_message.Message,), dict( + DESCRIPTOR = _HEARTBEATMESSAGE, + __module__ = 'presenter_message_pb2' + # @@protoc_insertion_point(class_scope:ascend.presenter.proto.HeartbeatMessage) + )) +_sym_db.RegisterMessage(HeartbeatMessage) + +Coordinate = _reflection.GeneratedProtocolMessageType('Coordinate', (_message.Message,), dict( + DESCRIPTOR = _COORDINATE, + __module__ = 'presenter_message_pb2' + # @@protoc_insertion_point(class_scope:ascend.presenter.proto.Coordinate) + )) +_sym_db.RegisterMessage(Coordinate) + +Rectangle_Attr = _reflection.GeneratedProtocolMessageType('Rectangle_Attr', (_message.Message,), dict( + DESCRIPTOR = _RECTANGLE_ATTR, + __module__ = 'presenter_message_pb2' + # @@protoc_insertion_point(class_scope:ascend.presenter.proto.Rectangle_Attr) + )) +_sym_db.RegisterMessage(Rectangle_Attr) + +PresentImageRequest = _reflection.GeneratedProtocolMessageType('PresentImageRequest', (_message.Message,), dict( + DESCRIPTOR = _PRESENTIMAGEREQUEST, + __module__ = 'presenter_message_pb2' + # @@protoc_insertion_point(class_scope:ascend.presenter.proto.PresentImageRequest) + )) +_sym_db.RegisterMessage(PresentImageRequest) + +PresentImageResponse = _reflection.GeneratedProtocolMessageType('PresentImageResponse', (_message.Message,), dict( + DESCRIPTOR = _PRESENTIMAGERESPONSE, + __module__ = 'presenter_message_pb2' + # @@protoc_insertion_point(class_scope:ascend.presenter.proto.PresentImageResponse) + )) +_sym_db.RegisterMessage(PresentImageResponse) + + +# @@protoc_insertion_point(module_scope) diff --git a/atlas_utils/presenteragent/socket_client.py b/atlas_utils/presenteragent/socket_client.py new file mode 100644 index 0000000..198ffc2 --- /dev/null +++ b/atlas_utils/presenteragent/socket_client.py @@ -0,0 +1,119 @@ +# !/usr/bin/env python +# -*- coding:utf-8 -*- + +import threading +import socket +import time +import struct +import time + +from .presenter_datatype import * + + + +class AgentSocket(object): + def __init__(self, server_ip, port): + self._server_address = (server_ip, port) + self._sock_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + + def connect(self): + ret = 0 + for i in range(0,5): + ret = self._sock_client.connect_ex(self._server_address) + if ret == 0: + break + time.sleep(0.2) + return ret + + def _read_socket(self, read_len): + has_read_len = 0 + read_buf = b'' + total_buf = b'' + + while has_read_len != read_len: + try: + read_buf = self._sock_client.recv(read_len - has_read_len) + except socket.error: + print("ERROR:Read socket failed") + return False, None + if read_buf == b'': + return False, None + total_buf += read_buf + has_read_len = len(total_buf) + + return True, total_buf + + def _read_msg_head(self, read_len): + ret, msg_head = self._read_socket(read_len) + #print("msg head data is :", msg_head) + if not ret: + print("ERROR:socket receive msg head null") + return None, None + + # in Struct(), 'I' is unsigned int, 'B' is unsigned char + msg_head_data = struct.Struct('IB') + (msg_total_len, msg_name_len) = msg_head_data.unpack(msg_head) + msg_total_len = socket.ntohl(msg_total_len) + #print("msg total length is :", msg_total_len) + #print("msg name is :", msg_name_len) + return msg_total_len, msg_name_len + + def _read_msg_name(self, msg_name_len): + ret, msg_name = self._read_socket(msg_name_len) + #print("direct msg name is :", msg_name) + if not ret: + print("ERROR:socket receive msg name null") + return False, None + try: + msg_name = msg_name.decode("utf-8") + #print("decode msg name is :", msg_name) + except Exception as e: + print("ERROR:msg name decode to utf-8 error") + return False, None + + return True, msg_name + + def _read_msg_body(self, msg_body_len): + #print("msg body length is :", msg_body_len) + ret, msg_body = self._read_socket(msg_body_len) + if not ret: + print("ERROR:socket receive msg body null") + return False, None + return True, msg_body + + def recv_msg(self): + # Step1: read msg head + msg_total_len, msg_name_len = self._read_msg_head(5) + if msg_total_len is None: + print("ERROR:msg total len is None.") + return None + + # Step2: read msg name + ret, msg_name = self._read_msg_name(msg_name_len) + if not ret: + return None + + # Step3: read msg body + msg_body_len = msg_total_len - 5 - msg_name_len + if msg_body_len < 0: + print("ERROR:msg total len is 0") + return None + ret, msg_body = self._read_msg_body(msg_body_len) + if not ret: + return None + + return msg_name, msg_body + + + def send_msg(self, data): + try: + self._sock_client.sendall(data) + except Exception as e: + print("ERROR:Send msg failed") + return 1 + return 0 + + def close(self): + self._sock_client.shutdown(socket.SHUT_RDWR) + self._sock_client.close() diff --git a/atlas_utils/utils.py b/atlas_utils/utils.py new file mode 100644 index 0000000..80f3bd1 --- /dev/null +++ b/atlas_utils/utils.py @@ -0,0 +1,71 @@ +import acl +from atlas_utils.constants import * +from atlas_utils.lib.atlasutil_so import libatlas + +def check_ret(message, ret): + if ret != ACL_ERROR_NONE: + raise Exception("{} failed ret={}" + .format(message, ret)) + +def copy_data_device_to_host(device_data, data_size): + host_buffer, ret = acl.rt.malloc_host(data_size) + if ret != ACL_ERROR_NONE: + print("Malloc host memory failed, error: ", ret) + return None + + ret = acl.rt.memcpy(host_buffer, data_size, + device_data, data_size, + ACL_MEMCPY_DEVICE_TO_HOST) + if ret != ACL_ERROR_NONE: + print("Copy device data to host memory failed, error: ", ret) + acl.rt.free_host(host_buffer) + return None + + return host_buffer + +def copy_data_device_to_device(device_data, data_size): + device_buffer, ret = acl.rt.malloc(data_size, ACL_MEM_MALLOC_NORMAL_ONLY) + if ret != ACL_ERROR_NONE: + print("Malloc device memory failed, error: ", ret) + return None + + ret = acl.rt.memcpy(device_buffer, data_size, + device_data, data_size, + ACL_MEMCPY_DEVICE_TO_DEVICE) + if ret != ACL_ERROR_NONE: + print("Copy device data to device memory failed, error: ", ret) + acl.rt.free(device_buffer) + return None + + return device_buffer + +def copy_data_host_to_device(host_data, data_size): + device_buffer, ret = acl.rt.malloc(data_size, ACL_MEM_MALLOC_NORMAL_ONLY) + if ret != ACL_ERROR_NONE: + print("Malloc device memory failed, error: ", ret) + return None + + ret = acl.rt.memcpy(device_buffer, data_size, + host_data, data_size, + ACL_MEMCPY_HOST_TO_DEVICE) + if ret != ACL_ERROR_NONE: + print("Copy device data to device memory failed, error: ", ret) + acl.rt.free(device_buffer) + return None + + return device_buffer + +def align_up(value, align): + return int(int((value + align - 1) / align) * align) + +def align_up16(value): + return align_up(value, 16) + +def align_up2(value): + return align_up(value, 2) + +def yuv420sp_size(width, height): + return width * height * 3 // 2 + +def unpack_bytes(dest, dest_size, src, src_size): + libatlas.UnpackFloatByteArray(dest, dest_size, src, src_size) diff --git a/body_pose.conf b/body_pose.conf new file mode 100644 index 0000000..ff8f166 --- /dev/null +++ b/body_pose.conf @@ -0,0 +1,20 @@ +[baseconf] +# A socket server address to communicate with presenter agent +presenter_server_ip=192.168.1.223 + +# The port of presenter agent and server communicate with +presenter_server_port=7006 + +#the ip in presenter server view web url +presenter_view_ip=192.168.1.223 + +#the ip of atlas200dk board connect with presenter server +#presenter_agent_ip=192.168.1.2 +atlas200dk_board_ip=192.168.1.2 + +#view entry label in presenter server view web +channel_name=video + +#the data type that send to presenter server from agent, 0:image, 1:video +content_type=1 + diff --git a/body_pose.log b/body_pose.log new file mode 100644 index 0000000..f0d5e9b --- /dev/null +++ b/body_pose.log @@ -0,0 +1,22 @@ +2020-09-08 22:16:36,023-INFO-body_pose_server.py:222 presenter server is starting... +2020-09-08 22:16:36,024-INFO-presenter_server.py:79 presenter server starting, type: body_pose +2020-09-08 22:17:25,532-INFO-presenter_socket_server.py:255 create new connection:client-ip:192.168.1.2, client-port:53428, fd:17 +2020-09-08 22:17:26,028-ERROR-presenter_socket_server.py:354 channel name video is not exist. +2020-09-08 22:17:26,030-INFO-channel_manager.py:216 register channel: video +2020-09-08 22:17:26,032-INFO-channel_handler.py:215 create videothread-video... +2020-09-08 22:17:26,032-INFO-channel_manager.py:127 create channel resource, channel_name:video, channel_fd:17, media_type:video +2020-09-08 22:17:34,188-ERROR-presenter_socket_server.py:142 socket 17 receive msg head null +2020-09-08 22:17:34,188-ERROR-presenter_socket_server.py:204 msg_total_len is None. +2020-09-08 22:17:34,189-INFO-body_pose_server.py:60 clean fd:17, conns:{17: } +2020-09-08 22:17:34,191-INFO-channel_handler.py:124 videothread-video set _close_thread_switch True +2020-09-08 22:17:34,191-INFO-channel_manager.py:139 clean channel: video's resource +2020-09-08 22:17:34,192-INFO-channel_handler.py:226 Stop thread:videothread-video. +2020-09-08 22:18:03,748-INFO-presenter_socket_server.py:255 create new connection:client-ip:192.168.1.2, client-port:53438, fd:17 +2020-09-08 22:18:04,245-INFO-channel_handler.py:215 create videothread-video... +2020-09-08 22:18:04,245-INFO-channel_manager.py:127 create channel resource, channel_name:video, channel_fd:17, media_type:video +2020-09-08 22:18:53,726-ERROR-presenter_socket_server.py:142 socket 17 receive msg head null +2020-09-08 22:18:53,727-ERROR-presenter_socket_server.py:204 msg_total_len is None. +2020-09-08 22:18:53,727-INFO-body_pose_server.py:60 clean fd:17, conns:{17: } +2020-09-08 22:18:53,727-INFO-channel_handler.py:124 videothread-video set _close_thread_switch True +2020-09-08 22:18:53,727-INFO-channel_manager.py:139 clean channel: video's resource +2020-09-08 22:18:53,727-INFO-channel_handler.py:226 Stop thread:videothread-video. diff --git a/code_image/main.py b/code_image/main.py new file mode 100644 index 0000000..0601fac --- /dev/null +++ b/code_image/main.py @@ -0,0 +1,57 @@ +import os +import cv2 +import numpy as np +import argparse +import sys +sys.path.append('..') +from model_processor import ModelProcessor +import acl +from acl_resource import AclResource + +MODEL_PATH = "../model/body_pose.om" +DATA_PATH = './tennis_player.jpg' + +def execute(model_path, frames_input_src, output_dir): + + + ## Initialization ## + #initialize acl runtime + acl_resource = AclResource() + acl_resource.init() + + ## Prepare Model ## + # parameters for model path and model inputs + model_parameters = { + 'model_dir': model_path, + 'width': 368, # model input width + 'height': 368, # model input height + } + # perpare model instance: init (loading model from file to memory) + # model_processor: preprocessing + model inference + postprocessing + model_processor = ModelProcessor(acl_resource, model_parameters) + + ## Get Input ## + # Read the image input using OpenCV + img_original = cv2.imread(args.frames_input_src) + + ## Model Prediction ## + # model_processor.predict: processing + model inference + postprocessing + # canvas: the picture overlayed with human body joints and limbs + canvas = model_processor.predict(img_original) + + # Save the detected results + cv2.imwrite(os.path.join(args.output_dir, 'Result_Pose.jpg'), canvas) + + +if __name__ == '__main__': + description = 'Load a model for human pose estimation' + parser = argparse.ArgumentParser(description=description) + parser.add_argument('--model', type=str, default=MODEL_PATH) + parser.add_argument('--frames_input_src', type=str,default=DATA_PATH, help="Directory path for image") + parser.add_argument('--output_dir', type=str, default='./outputs', help="Output Path") + + args = parser.parse_args() + if not os.path.exists(args.output_dir): + os.makedirs(args.output_dir) + + execute(args.model, args.frames_input_src, args.output_dir) diff --git a/code_image/tennis_player.jpg b/code_image/tennis_player.jpg new file mode 100644 index 0000000..82e5107 Binary files /dev/null and b/code_image/tennis_player.jpg differ diff --git a/code_live/main.py b/code_live/main.py new file mode 100644 index 0000000..4026269 --- /dev/null +++ b/code_live/main.py @@ -0,0 +1,107 @@ +import random +import os +import cv2 +import numpy as np +import argparse +import sys +sys.path.append('..') +from model_processor import ModelProcessor +from atlas_utils.camera import Camera +from atlas_utils import presenteragent +from atlas_utils.acl_image import AclImage +import acl +from acl_resource import AclResource + +MODEL_PATH = "../model/body_pose.om" +BODYPOSE_CONF="../body_pose.conf" +CAMERA_FRAME_WIDTH = 1280 +CAMERA_FRAME_HEIGHT = 720 + +def execute(model_path): + + ## Initialization ## + #initialize acl runtime + acl_resource = AclResource() + acl_resource.init() + + ## Prepare Model ## + # parameters for model path and model inputs + model_parameters = { + 'model_dir': model_path, + 'width': 368, # model input width + 'height': 368, # model input height + } + # perpare model instance: init (loading model from file to memory) + # model_processor: preprocessing + model inference + postprocessing + model_processor = ModelProcessor(acl_resource, model_parameters) + + ## Get Input ## + # Initialize Camera + cap = Camera(id = 0, fps = 10) + + ## Set Output ## + # open the presenter channel + chan = presenteragent.presenter_channel.open_channel(BODYPOSE_CONF) + if chan == None: + print("Open presenter channel failed") + return + + + + while True: + ## Read one frame from Camera ## + img_original = cap.read() + if not img_original: + print('Error: Camera read failed') + break + # Camera Input (YUV) to RGB Image + image_byte = img_original.tobytes() + image_array = np.frombuffer(image_byte, dtype=np.uint8) + img_original = YUVtoRGB(image_array) + img_original = cv2.flip(img_original,1) + + ## Model Prediction ## + # model_processor.predict: processing + model inference + postprocessing + # canvas: the picture overlayed with human body joints and limbs + canvas = model_processor.predict(img_original) + + ## Present Result ## + # convert to jpeg image for presenter server display + _,jpeg_image = cv2.imencode('.jpg',canvas) + # construct AclImage object for presenter server + jpeg_image = AclImage(jpeg_image, img_original.shape[0], img_original.shape[1], jpeg_image.size) + # send to presenter server + chan.send_detection_data(img_original.shape[0], img_original.shape[1], jpeg_image, []) + + # release the resources + cap.release() + +def YUVtoRGB(byteArray): + e = 1280*720 + Y = byteArray[0:e] + Y = np.reshape(Y, (720,1280)) + + s = e + V = byteArray[s::2] + V = np.repeat(V, 2, 0) + V = np.reshape(V, (360,1280)) + V = np.repeat(V, 2, 0) + + U = byteArray[s+1::2] + U = np.repeat(U, 2, 0) + U = np.reshape(U, (360,1280)) + U = np.repeat(U, 2, 0) + + RGBMatrix = (np.dstack([Y,U,V])).astype(np.uint8) + RGBMatrix = cv2.cvtColor(RGBMatrix, cv2.COLOR_YUV2RGB, 3) + return RGBMatrix + + +if __name__ == '__main__': + + description = 'Load a model for human pose estimation' + parser = argparse.ArgumentParser(description=description) + parser.add_argument('--model', type=str, default=MODEL_PATH) + args = parser.parse_args() + + execute(args.model) diff --git a/code_video/main.py b/code_video/main.py new file mode 100644 index 0000000..6066fd4 --- /dev/null +++ b/code_video/main.py @@ -0,0 +1,106 @@ +import random +import os +import cv2 +import numpy as np +import argparse +import sys +sys.path.append('..') +from model_processor import ModelProcessor +from atlas_utils.camera import Camera +from atlas_utils import presenteragent +from atlas_utils.acl_image import AclImage +import acl +from acl_resource import AclResource + +MODEL_PATH = "../model/body_pose.om" +BODYPOSE_CONF="../body_pose.conf" +CAMERA_FRAME_WIDTH = 1280 +CAMERA_FRAME_HEIGHT = 720 +DATA_PATH = './yoga.mp4' + + +def execute(model_path, frames_input_src, output_dir, is_presenter_server): + + ## Initialization ## + #initialize acl runtime + acl_resource = AclResource() + acl_resource.init() + + ## Prepare Model ## + # parameters for model path and model inputs + model_parameters = { + 'model_dir': model_path, + 'width': 368, # model input width + 'height': 368, # model input height + } + # perpare model instance: init (loading model from file to memory) + # model_processor: preprocessing + model inference + postprocessing + model_processor = ModelProcessor(acl_resource, model_parameters) + + ## Get Input ## + # Read the video input using OpenCV + cap = cv2.VideoCapture(frames_input_src) + + ## Set Output ## + if is_presenter_server: + # if using presenter server, then open the presenter channel + chan = presenteragent.presenter_channel.open_channel(BODYPOSE_CONF) + if chan == None: + print("Open presenter channel failed") + return + else: + # if saving result as video file (mp4), then set the output video writer using opencv + video_output_path = '{}/demo-{}-{}.mp4'.format(output_dir, os.path.basename(frames_input_src), str(random.randint(1, 100001))) + video_writer = cv2.VideoWriter(video_output_path, 0x7634706d, 25, + (1280, 720)) + if video_writer == None: + print('Error: cannot get video writer from openCV') + + + while(cap.isOpened()): + ## Read one frame of the input video ## + ret, img_original = cap.read() + + if not ret: + print('Cannot read more, Reach the end of video') + break + + ## Model Prediction ## + # model_processor.predict: processing + model inference + postprocessing + # canvas: the picture overlayed with human body joints and limbs + canvas = model_processor.predict(img_original) + + ## Present Result ## + if is_presenter_server: + # convert to jpeg image for presenter server display + _,jpeg_image = cv2.imencode('.jpg',canvas) + # construct AclImage object for presenter server + jpeg_image = AclImage(jpeg_image, img_original.shape[0], img_original.shape[1], jpeg_image.size) + # send to presenter server + chan.send_detection_data(img_original.shape[0], img_original.shape[1], jpeg_image, []) + + else: + # save to video + video_writer.write(canvas) + + # release the resources + cap.release() + if not is_presenter_server: + video_writer.release() + + + + +if __name__ == '__main__': + + description = 'Load a model for human pose estimation' + parser = argparse.ArgumentParser(description=description) + parser.add_argument('--model', type=str, default=MODEL_PATH) + parser.add_argument('--frames_input_src', type=str,default=DATA_PATH, help="Directory path for video.") + parser.add_argument('--output_dir', type=str, default='./outputs', help="Output Path") + parser.add_argument('--is_presenter_server', type=bool, default=False, help="Display on presenter server or save to a video mp4 file (T/F)") + args = parser.parse_args() + if not os.path.exists(args.output_dir): + os.makedirs(args.output_dir) + + execute(args.model, args.frames_input_src, args.output_dir, args.is_presenter_server) diff --git a/code_video/yoga.mp4 b/code_video/yoga.mp4 new file mode 100644 index 0000000..6ebfc1e Binary files /dev/null and b/code_video/yoga.mp4 differ diff --git a/figures/IP地址示例.png b/figures/IP地址示例.png new file mode 100644 index 0000000..a50e019 Binary files /dev/null and b/figures/IP地址示例.png differ diff --git a/figures/bashrc.png b/figures/bashrc.png new file mode 100644 index 0000000..ae9f663 Binary files /dev/null and b/figures/bashrc.png differ diff --git a/figures/files_copy.png b/figures/files_copy.png new file mode 100644 index 0000000..0b622b3 Binary files /dev/null and b/figures/files_copy.png differ diff --git a/figures/pose_detected.jpg b/figures/pose_detected.jpg new file mode 100644 index 0000000..0c27e8f Binary files /dev/null and b/figures/pose_detected.jpg differ diff --git a/figures/presenter.png b/figures/presenter.png new file mode 100644 index 0000000..8320b9e Binary files /dev/null and b/figures/presenter.png differ diff --git a/figures/result.png b/figures/result.png new file mode 100644 index 0000000..f4f3ded Binary files /dev/null and b/figures/result.png differ diff --git a/figures/主页显示.png b/figures/主页显示.png new file mode 100644 index 0000000..52997fc Binary files /dev/null and b/figures/主页显示.png differ diff --git a/model/.keep b/model/.keep new file mode 100644 index 0000000..e69de29 diff --git a/model/body_pose.om b/model/body_pose.om new file mode 100644 index 0000000..5de4e60 Binary files /dev/null and b/model/body_pose.om differ diff --git a/model_processor.py b/model_processor.py new file mode 100644 index 0000000..0c3437b --- /dev/null +++ b/model_processor.py @@ -0,0 +1,59 @@ +import os +import cv2 +import numpy as np +import argparse +import sys + +sys.path.append('../') + +from src.pose_decode import decode_pose +from acl_model import Model + +heatmap_width = 92 +heatmap_height = 92 + + +class ModelProcessor: + + def __init__(self, acl_resource, params): + self._acl_resource = acl_resource + self.params = params + self._model_width = params['width'] + self._model_height = params['height'] + + assert 'model_dir' in params and params['model_dir'] is not None, 'Review your param: model_dir' + assert os.path.exists(params['model_dir']), "Model directory doesn't exist {}".format(params['model_dir']) + + # load model from path, and get model ready for inference + self.model = Model(acl_resource, params['model_dir']) + + def predict(self, img_original): + + #preprocess image to get 'model_input' + model_input = self.preprocess(img_original) + + # execute model inference + result = self.model.execute([model_input]) + + # postprocessing: use the heatmaps (the second output of model) to get the joins and limbs for human body + # Note: the model has multiple outputs, here we used a simplified method, which only uses heatmap for body joints + # and the heatmap has shape of [1,14], each value correspond to the position of one of the 14 joints. + # The value is the index in the 92*92 heatmap (flatten to one dimension) + heatmaps = result[1] + # calculate the scale of original image over heatmap, Note: image_original.shape[0] is height + scale = np.array([img_original.shape[1] / heatmap_width, img_original.shape[0]/ heatmap_height]) + + canvas = decode_pose(heatmaps[0], scale, img_original) + + return canvas + + def preprocess(self,img_original): + ''' + preprocessing: resize image to model required size, and normalize value between [0,1] + ''' + scaled_img_data = cv2.resize(img_original, (self._model_width, self._model_height)) + preprocessed_img = np.asarray(scaled_img_data, dtype=np.float32) / 255. + + return preprocessed_img + + diff --git a/presenterserver/.idea/misc.xml b/presenterserver/.idea/misc.xml new file mode 100644 index 0000000..c6dbc08 --- /dev/null +++ b/presenterserver/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/presenterserver/.idea/modules.xml b/presenterserver/.idea/modules.xml new file mode 100644 index 0000000..81c9276 --- /dev/null +++ b/presenterserver/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/presenterserver/.idea/presenterserver.iml b/presenterserver/.idea/presenterserver.iml new file mode 100644 index 0000000..c06b0e7 --- /dev/null +++ b/presenterserver/.idea/presenterserver.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/presenterserver/.idea/public_sys-resources/icon-caution.gif b/presenterserver/.idea/public_sys-resources/icon-caution.gif new file mode 100644 index 0000000..6e90d7c Binary files /dev/null and b/presenterserver/.idea/public_sys-resources/icon-caution.gif differ diff --git a/presenterserver/.idea/public_sys-resources/icon-danger.gif b/presenterserver/.idea/public_sys-resources/icon-danger.gif new file mode 100644 index 0000000..6e90d7c Binary files /dev/null and b/presenterserver/.idea/public_sys-resources/icon-danger.gif differ diff --git a/presenterserver/.idea/public_sys-resources/icon-note.gif b/presenterserver/.idea/public_sys-resources/icon-note.gif new file mode 100644 index 0000000..6314297 Binary files /dev/null and b/presenterserver/.idea/public_sys-resources/icon-note.gif differ diff --git a/presenterserver/.idea/public_sys-resources/icon-notice.gif b/presenterserver/.idea/public_sys-resources/icon-notice.gif new file mode 100644 index 0000000..86024f6 Binary files /dev/null and b/presenterserver/.idea/public_sys-resources/icon-notice.gif differ diff --git a/presenterserver/.idea/public_sys-resources/icon-tip.gif b/presenterserver/.idea/public_sys-resources/icon-tip.gif new file mode 100644 index 0000000..93aa720 Binary files /dev/null and b/presenterserver/.idea/public_sys-resources/icon-tip.gif differ diff --git a/presenterserver/.idea/public_sys-resources/icon-warning.gif b/presenterserver/.idea/public_sys-resources/icon-warning.gif new file mode 100644 index 0000000..6e90d7c Binary files /dev/null and b/presenterserver/.idea/public_sys-resources/icon-warning.gif differ diff --git a/presenterserver/.idea/workspace.xml b/presenterserver/.idea/workspace.xml new file mode 100644 index 0000000..c8e1363 --- /dev/null +++ b/presenterserver/.idea/workspace.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + true + DEFINITION_ORDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "; + } + if (btnFlag == 0) { + div += "
OK
"; + } else if (btnFlag == 1) { + div += "
OKCancel
"; + } + div += ""; + $mask.html(div); + $mask.find("input").val($mask.find("input").val()); + if (content.type == 1) { + $mask.find(".ok").on("click", function() { + var retText = $mask.find("input").val(); + hide(); + if (ok) { + ok(retText); + } + }); + $mask.find(".cancel").on("click", function() { + var retText = $mask.find("input").val(); + hide(); + if (cancel) { + cancel(retText); + } + }); + } else { + $mask.find(".ok").on("click", function() { + hide(); + if (ok) { + ok(); + } + }); + $mask.find(".cancel").on("click", function() { + hide(); + if (cancel) { + cancel(); + } + }); + } + } + + function calcBoxPos() { + var $box = $mask.find("#dlg-box"); + var mask_w = $mask.outerWidth(); + var mask_h = $mask.outerHeight(); + var box_w = $box.outerWidth(); + var box_h = $box.outerHeight(); + var pos_left = (mask_w - box_w) / 2 + "px"; + var pos_top = (mask_h - box_h) / 2 + "px"; + $box.css("left", pos_left).css("top", pos_top); + } + + function show() { + $("body").prepend($mask); + $mask.css("display", "block"); + if ($mask.find("input") && $mask.find("input")[0]) { + $mask.find("input")[0].focus(); + } + calcBoxPos(); + $(window).resize(calcBoxPos); + } + + function hide() { + $mask.css("display", "none"); + $mask.remove(); + } + + return { + hide: hide, + tip: function(title, text, ok) { + initHtml(title, { type: 0, text: text }, 0, ok, null); + show(); + }, + input: function(title, text, placeholder, ok, cancel) { + initHtml(title, { type: 1, text: text, placeholder: placeholder }, 1, ok, cancel); + show(); + }, + confirm: function(title, text, ok, cancel) { + initHtml(title, { type: 0, text: text }, 1, ok, cancel); + show(); + } + } +})(); \ No newline at end of file diff --git a/presenterserver/body_pose/ui/static/js/dialog.min.js b/presenterserver/body_pose/ui/static/js/dialog.min.js new file mode 100644 index 0000000..b2c094c --- /dev/null +++ b/presenterserver/body_pose/ui/static/js/dialog.min.js @@ -0,0 +1 @@ +var dialog = (function Dialog() { var e = $("
"); function d(j, h, i, f, g) { var k = ""; k += "
"; k += "

" + j + "

"; if (h.type == 0) { k += "

" + h.text + "

" } else { if (h.type == 1) { k += "" } } if (i == 0) { k += "
OK
" } else { if (i == 1) { k += "
OKCancel
" } } k += "
"; e.html(k); e.find("input").val(e.find("input").val()); if (h.type == 1) { e.find(".ok").on("click", function() { var l = e.find("input").val(); b(); if (f) { f(l) } }); e.find(".cancel").on("click", function() { var l = e.find("input").val(); b(); if (g) { g(l) } }) } else { e.find(".ok").on("click", function() { b(); if (f) { f() } }); e.find(".cancel").on("click", function() { b(); if (g) { g() } }) } } function c() { var k = e.find("#dlg-box"); var h = e.outerWidth(); var j = e.outerHeight(); var l = k.outerWidth(); var g = k.outerHeight(); var f = (h - l) / 2 + "px"; var i = (j - g) / 2 + "px"; k.css("left", f).css("top", i) } function a() { $("body").prepend(e); e.css("display", "block"); if (e.find("input") && e.find("input")[0]) { e.find("input")[0].focus() } c(); $(window).resize(c) } function b() { e.css("display", "none"); e.remove() } return { hide: b, tip: function(h, g, f) { d(h, { type: 0, text: g }, 0, f, null); a() }, input: function(j, i, h, f, g) { d(j, { type: 1, text: i, placeholder: h }, 1, f, g); a() }, confirm: function(i, h, f, g) { d(i, { type: 0, text: h }, 1, f, g); a() } } })(); \ No newline at end of file diff --git a/presenterserver/body_pose/ui/static/js/index.js b/presenterserver/body_pose/ui/static/js/index.js new file mode 100644 index 0000000..e69de29 diff --git a/presenterserver/body_pose/ui/static/js/jquery-1.10.2.min.js b/presenterserver/body_pose/ui/static/js/jquery-1.10.2.min.js new file mode 100644 index 0000000..8569bc4 --- /dev/null +++ b/presenterserver/body_pose/ui/static/js/jquery-1.10.2.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
t
",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t +}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); +u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("