Repository: activeloopai/deeplake

Issue:
Fixes in to_tensorflow method
Observed a couple of problems while converting stored datasets to TensorFlow format that need some small fixes.

to_tensorflow fails when the meta information for a tensor includes dtype="object" ("object" dtype has been used for images, area, id, bbox in Coco dataset - https://github.com/activeloopai/Hub/blob/master/examples/coco/upload_coco2017.py#L24) 
A fix for this is to keep the dtype="uint8" or something similar while uploading. The Coco example needs to be updated to reflect this.

to_tensorflow also fails when it gets shape=(1,) in meta and the actual object has multiple dimensions, for example, an image.
This can be fixed by commenting out this line https://github.com/activeloopai/Hub/blob/master/hub/collections/dataset/core.py#L633, which will set the output_shapes as None by default. 

to_pytorch works fine in both the above cases.



Discussion:
> to_tensorflow fails when the meta information for a tensor includes dtype="object" ("object" dtype has been used for images, area, id, bbox in Coco dataset - https://github.com/activeloopai/Hub/blob/master/examples/coco/upload_coco2017.py#L24)
A fix for this is to keep the dtype="uint8" or something similar while uploading. The Coco example needs to be updated to reflect this.

Changing the values in the dict returned by meta would work?

> The Coco example needs to be updated to reflect this.

And I did not get this part...

> to_tensorflow also fails when it gets shape=(1,) in meta and the actual object has multiple dimensions, for example, an image.
This can be fixed by commenting out this line https://github.com/activeloopai/Hub/blob/master/hub/collections/dataset/core.py#L633, which will set the output_shapes as None by default.

After I comment out the line, how do I test it?


> Changing the values in the dict returned by meta would work?


Yeah should work by changing the dtype in meta, but it's better to change it in _call_ as well, to make it obvious for any user

>And I did not get this part...

When the user doesn't specify the shape and just mentions shape = (1,), to_tensorflow strictly expects shape as (1,). If we comment out https://github.com/activeloopai/Hub/blob/master/hub/collections/dataset/core.py#L633, it will accept any shape given to it.

>After I comment out the line, how do I test it?

Once you make both of these changes, try storing the dataset using https://github.com/activeloopai/Hub/blob/master/examples/coco/upload_coco2017.py.
 (Tip:- if you use ds.store("./path/to/directory"), the dataset will get stored locally, instead of online, might save you some time)
Once you have stored it, try loading it with a file similar to:- https://github.com/activeloopai/Hub/blob/master/examples/load_tf.py
If everything is fine, you shouldn't face any issues in loading.

Also try loading with Pytorch, to ensure that the changes didn't break that:-
https://github.com/activeloopai/Hub/blob/master/examples/load_pytorch.py

Let me know if you face any issues.
> > And I did not get this part...

Actually @AbhinavTuli  by that I meant I did not get this (below) part
> The Coco example needs to be updated to reflect this

So by example, do you mean the example py file (https://github.com/activeloopai/Hub/blob/master/examples/coco/upload_coco2017.py) or example dataset? 😅 
Essentially both. You'll update the py file and then upload the dataset using the modified file, so both will get updated.
Makes sense, got it.

While making changes in the meta file, you mentioned "unit8" or something similar...
The changes I currently made (on my local system are) 
area  --> uint32
bbox --> uint16
id --> uint32
image --> uint32

The reason, why I chose these values, is because, after a quick google search on coco, I got a feeling that uint8 won't be sufficient.
But then, I am not sure what these values mean, so here I have 2 questions
1. Are my dtype values correct/okay, if not what should they be?
2. What is their significance? 
 
The dtype is essentially similar to numpy dtype that we keep track of as metadata, it helps us in storing chunks of data efficiently (in case chunk_size isn't explicitly mentioned). It also helps us in converting from hub format to other formats as well.

The dtypes you chose should be fine as long as the entire range of values fits in them, which I think they will.
Try saving the dataset locally to see if everything is working. It'll save you some time. Use ds.store("./path") for this. 
Once everything is working you can upload to hub.

Resolving patch:
diff --git a/examples/coco/upload_coco2017.py b/examples/coco/upload_coco2017.py
--- a/examples/coco/upload_coco2017.py
+++ b/examples/coco/upload_coco2017.py
@@ -25,7 +25,7 @@ def meta(self):
             "iscrowd": {"shape": (1,), "dtype": "uint8", "chunksize": 1000},
             "image_id": {"shape": (1,), "dtype": "int64"},
             "bbox": {"shape": (1,), "dtype": "uint16", "chunksize": 1000},
-            "category_id": {"shape": (1,), "dtype": "int64", "chunksize": 1000},
+            "category_id": {"shape": (1,), "dtype": "uint32", "chunksize": 1000},
             "id": {"shape": (1,), "dtype": "uint32", "chunksize": 1000},
             "image": {"shape": (1,), "dtype": "uint32", "chunksize": 100},
         }
@@ -36,7 +36,7 @@ def __call__(self, input):
             # print(f"Image id: {input['image_id']}")
             ds["image_id"] = input["image_id"]
             info = input["info"]
-            ds["image"] = np.empty(1, np.uint32)
+            ds["image"] = np.empty(1, "uint32")
             ds["image"][0] = np.array(
                 Image.open(
                     os.path.join(
@@ -44,14 +44,14 @@ def __call__(self, input):
                         get_image_name(self._args, self._tag, input["image_id"]),
                     )
                 ),
-                dtype=np.uint32,
+                dtype="uint32",
             )
-            ds["segmentation"] = np.empty(1, np.uint32)
-            ds["area"] = np.empty(1, np.uint32)
-            ds["iscrowd"] = np.empty(1, np.uint8)
-            ds["bbox"] = np.empty(1, np.np.uint16)
-            ds["category_id"] = np.empty(1, np.uint8)
-            ds["id"] = np.empty(1, np.uint32)
+            ds["segmentation"] = np.empty(1, "uint32")
+            ds["area"] = np.empty(1, "uint32")
+            ds["iscrowd"] = np.empty(1, "uint8")
+            ds["bbox"] = np.empty(1, "uint16")
+            ds["category_id"] = np.empty(1, "uint32")
+            ds["id"] = np.empty(1, "uint32")
 
             ds["segmentation"][0] = [anno["segmentation"] for anno in info]
             ds["area"][0] = [anno["area"] for anno in info]
diff --git a/hub/collections/dataset/core.py b/hub/collections/dataset/core.py
--- a/hub/collections/dataset/core.py
+++ b/hub/collections/dataset/core.py
@@ -627,7 +627,7 @@ def tf_dtype(np_dtype):
         return tf.data.Dataset.from_generator(
             tf_gen,
             output_types=output_types,
-            # output_shapes=output_shapes, # commenting out this so TF takes the shape whatever is passed
+            # output_shapes=output_shapes,
         )
 
 


Test patch: