Repository: huggingface/diffusers

Issue:
Color channel order for watermark embedding
### Describe the bug

The encoder from the invisible watermark library expects input images with the channel order BGR, which is the default in OpenCV. This can be seen [here](https://github.com/ShieldMnt/invisible-watermark/blob/68d0376d94a4701ed240af0841ec12e00676e325/imwatermark/maxDct.py#L21).

As far as I can see from [here](https://github.com/huggingface/diffusers/blob/3369bc810a09a52521bbf8cc1ec77df3a8c682a8/src/diffusers/pipelines/stable_diffusion_xl/watermark.py#L24), diffusers passes the images in RGB order.

The watermark encoder then converts the given image from BGR to YUV. When the image is passed with the wrong channel order, this will lead to unexpected U and V channel values.

### Reproduction

n/a

### Logs

_No response_

### System Info

Python 3.10, diffusers 0.24.0, invisible-watermark-0.2.0

### Who can help?

_No response_


Discussion:
Interesting. Have you seen how much does this impact the end outputs? 
This issue has been automatically marked as stale because it has not had recent activity. If you think this still needs to be addressed please comment on this thread.

Please note that issues that do not follow the [contributing guidelines](https://github.com/huggingface/diffusers/blob/main/CONTRIBUTING.md) are likely to be ignored.
@btlorch?
Sorry, I haven't had the time to evaluate the effects yet.
Several issues in this repository report visual defects due to the watermarking: #4014 #4035 #4074 

The issue is likely connected to the wrong channel order. The left image was created by SDXL without any watermark. The center image was watermarked with the wrong channel order, and the right image was watermarked with the correct channel order.

![2024_02_09-invisible_watermark_comparison](https://github.com/huggingface/diffusers/assets/11959715/7a4b62b6-7a04-4323-81a0-ccc77a33ce5f)

Here is a close up.

![2024_02_09-invisible_watermark_comparison_closeup](https://github.com/huggingface/diffusers/assets/11959715/0323143d-205f-44f8-b2ae-ee02ec3b635d)

Swapping the channel order does not eliminate all artifacts but the artifacts appear less pronounced, at least in this example.

Here is how the image was created.
```python
import matplotlib.pyplot as plt
from diffusers.pipelines.stable_diffusion_xl.watermark import WATERMARK_MESSAGE, WATERMARK_BITS, StableDiffusionXLWatermarker
from imwatermark import WatermarkEncoder
from diffusers import AutoPipelineForText2Image
from PIL import Image
import torch
import numpy as np

pipeline_text2image = AutoPipelineForText2Image.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float16, variant="fp16", use_safetensors=True
).to("cuda")

# Disable watermark
class NoWatermark:
    def apply_watermark(self, img):
        return img

pipeline_text2image.watermark = NoWatermark()

prompt = "Disney boy Low saturation Pixar Super details, double ponytails, anime waifu, Serafleur Art Style, divine cinematic edge lighting,soft focus,bokeh,chiaroscuro 8k,best quality ultra-detail 3d,c4d.blender,OCrenderer.cinematic lighting, ultra HD 3D rendering"
image = pipeline_text2image(prompt=prompt).images[0]
```

I swapped the channel order by replacing [this line](https://github.com/huggingface/diffusers/blob/3369bc810a09a52521bbf8cc1ec77df3a8c682a8/src/diffusers/pipelines/stable_diffusion_xl/watermark.py#L31) with
```python
# Convert RGB to BGR, pass BGR image to the encoder, and convert the watermarked image back to RGB
images = [self.encoder.encode(image[:, :, ::-1], "dwtDct")[:, :, ::-1] for image in images]
```
Cc: @yiyixuxu WDYT of incorporating this change?
OMG!! yes a PR please!

Resolving patch:
diff --git a/src/diffusers/pipelines/stable_diffusion_xl/watermark.py b/src/diffusers/pipelines/stable_diffusion_xl/watermark.py
--- a/src/diffusers/pipelines/stable_diffusion_xl/watermark.py
+++ b/src/diffusers/pipelines/stable_diffusion_xl/watermark.py
@@ -28,9 +28,15 @@ def apply_watermark(self, images: torch.FloatTensor):
 
         images = (255 * (images / 2 + 0.5)).cpu().permute(0, 2, 3, 1).float().numpy()
 
-        images = [self.encoder.encode(image, "dwtDct") for image in images]
+        # Convert RGB to BGR, which is the channel order expected by the watermark encoder.
+        images = images[:, :, :, ::-1]
 
-        images = torch.from_numpy(np.array(images)).permute(0, 3, 1, 2)
+        # Add watermark and convert BGR back to RGB
+        images = [self.encoder.encode(image, "dwtDct")[:, :, ::-1] for image in images]
+
+        images = np.array(images)
+
+        images = torch.from_numpy(images).permute(0, 3, 1, 2)
 
         images = torch.clamp(2 * (images / 255 - 0.5), min=-1.0, max=1.0)
         return images


Test patch: