Repository: tensorflow/addons

Issue:
Deprecate ImageProjectiveTransformV2
`ImageProjectiveTransformV2` seems to be integrated into core TF at https://github.com/tensorflow/tensorflow/commit/26992815bf7ee136c49f658596a9fbde7881d425 in Jan. We have to deprecate the use of it.

https://github.com/tensorflow/addons/blob/master/tensorflow_addons/custom_ops/image/cc/kernels/image_projective_transform_op.cc


Also, it was unclear to me that `ImageProjectiveTransformV2` has been integrated into TF many months ago. Maybe we should check new stuff in TF regularly. Thanks @bhack for pointing out!

cc @tensorflow/sig-addons-maintainers 


Discussion:
@bhack this issue is for what needs to be done to deprecate ImageProjectTransformV2. 

Please create a new issue to discuss how we can clarify the `tf.image`, `tfa.image`, and Keras image preprocessing relationship. Alternatively, add it to the meeting agenda. and let us know who you want us to try to get to the meeting for in person discussion.
> @bhack this issue is for what needs to be done to deprecate ImageProjectTransformV2.
> 
> Please create a new issue to discuss how we can clarify the `tf.image`, `tfa.image`, and Keras image preprocessing relationship. Alternatively, add it to the meeting agenda. and let us know who you want us to try to get to the meeting for in person discussion.

Yeah I believe those are separate issues. For `ImageProjectiveTransformV2` we could either 1) completely remove it from tfa.image, or 2) keep a reference to core.
I believe any redundancy is bad. If we have this in TF core, then I suggest we start with a deprecation warning and remove it gradually in the next version
Thanks @bhack  for bringing this to our attention! I believe we should replace the body of our implementation and call the one in TF while keeping the same API on our side for backward compatibility. If the TF implementation is the same as ours, the tests should pass. Though we should also keep the C++/CUDA code on our side for a while because if we remove the custom op, people trying to load their models will get an error.

But the biggest problem right now is that we can't access their implementation because it's not exposed in the public API (as far as I know). We could ask the TF team to expose it for TF 2.3? At least it doesn't seem that we're in a hurry.
> We could talk in a new ticket but I think that is the normality of the bottom-up process about recognize the single instance of an issue and trying to find the root process/generalizzation that it is creating this kind of issue.
> If we are working on the same or overlapped things but we don't know what we are planning on both sides we will go to generate more and more of these issues in image operations.

If we do it that way then we end up with the mess that is the AutoAugment issue. We don't want to have to dig through issues related to a larger topic to see the high level discussion. Feel free to link to the lower level issues in the higher level discussion not the other way around.
@bhack we totally agree with your point about duplication, but the best place to discuss a new process is a new standalone issue where is referenced all other issues where the pain point is appearing.
Cleaning up the discussion for the future readers.
I removed all the comments here and also the original comment on the @WindQAQ PR that generated this ISSUE.

Resolving patch:
diff --git a/tensorflow_addons/image/transform_ops.py b/tensorflow_addons/image/transform_ops.py
--- a/tensorflow_addons/image/transform_ops.py
+++ b/tensorflow_addons/image/transform_ops.py
@@ -16,13 +16,11 @@
 
 import tensorflow as tf
 from tensorflow_addons.image import utils as img_utils
-from tensorflow_addons.utils.resource_loader import LazySO
 from tensorflow_addons.utils.types import TensorLike
 from tensorflow_addons.image.utils import wrap, unwrap
 
 from typing import Optional
 
-_image_so = LazySO("custom_ops/image/_image_ops.so")
 
 _IMAGE_DTYPES = {
     tf.dtypes.uint8,
@@ -34,7 +32,6 @@
 }
 
 
-@tf.function
 def transform(
     images: TensorLike,
     transforms: TensorLike,
@@ -108,10 +105,10 @@ def transform(
                 % len(transforms.get_shape())
             )
 
-        output = _image_so.ops.addons_image_projective_transform_v2(
-            images,
-            output_shape=output_shape,
+        output = tf.raw_ops.ImageProjectiveTransformV2(
+            images=images,
             transforms=transforms,
+            output_shape=output_shape,
             interpolation=interpolation.upper(),
         )
         return img_utils.from_4D_image(output, original_ndims)
@@ -271,44 +268,6 @@ def angles_to_projective_transforms(
         )
 
 
-@tf.RegisterGradient("Addons>ImageProjectiveTransformV2")
-def _image_projective_transform_grad(op, grad):
-    """Computes the gradient for ImageProjectiveTransform."""
-    images = op.inputs[0]
-    transforms = op.inputs[1]
-    interpolation = op.get_attr("interpolation")
-
-    image_or_images = tf.convert_to_tensor(images, name="images")
-    transform_or_transforms = tf.convert_to_tensor(
-        transforms, name="transforms", dtype=tf.dtypes.float32
-    )
-
-    if image_or_images.dtype.base_dtype not in _IMAGE_DTYPES:
-        raise ValueError("Invalid dtype %s." % image_or_images.dtype)
-    if len(transform_or_transforms.get_shape()) == 1:
-        transforms = transform_or_transforms[None]
-    elif len(transform_or_transforms.get_shape()) == 2:
-        transforms = transform_or_transforms
-    else:
-        transforms = transform_or_transforms
-        raise ValueError(
-            "transforms should have rank 1 or 2, but got rank %d"
-            % len(transforms.get_shape())
-        )
-
-    # Invert transformations
-    transforms = flat_transforms_to_matrices(transforms=transforms)
-    inverse = tf.linalg.inv(transforms)
-    transforms = matrices_to_flat_transforms(inverse)
-    output = _image_so.ops.addons_image_projective_transform_v2(
-        images=grad,
-        transforms=transforms,
-        output_shape=tf.shape(image_or_images)[1:3],
-        interpolation=interpolation,
-    )
-    return [output, None, None]
-
-
 def rotate(
     images: TensorLike,
     angles: TensorLike,


Test patch: