Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
fe9cfe7
Add comprehensive image normalization documentation
mdlinville Aug 22, 2025
0f05c59
Fix broken relref links in Japanese and Korean media guides
mdlinville Aug 22, 2025
f988610
docs: address review feedback - use sentence case, avoid 'we', improv…
mdlinville Aug 22, 2025
5e1e10c
docs: address additional review feedback - use sentence case for head…
mdlinville Aug 22, 2025
437e947
Artifacts: When to use save() vs .log() (#1557)
ngrayluna Aug 22, 2025
a9f6290
Updated new view for lineage and dataset mismatch in keras tutorials …
knisar Aug 22, 2025
6093944
test: temporarily use mock preview URL for testing path matching
mdlinville Aug 21, 2025
fc5abb0
revert: remove mock URL test changes
mdlinville Aug 21, 2025
48196ce
docs(weave): fix broken relref links in Japanese and Korean media guides
mdlinville Aug 22, 2025
585403f
docs: address review feedback - move detailed normalization content t…
mdlinville Aug 25, 2025
fdebb34
feat: add image normalization demo notebook for testing and user exam…
mdlinville Aug 25, 2025
71f1092
refactor: move detailed normalization docs to dedicated guide and sim…
mdlinville Aug 25, 2025
eb9f9a5
docs: add visual contrast effects and guidance on when to use differe…
mdlinville Aug 25, 2025
bf500a7
docs: add improved image normalization demo notebook with detailed ex…
mdlinville Aug 25, 2025
6804a5c
docs: update notebook with detailed explanations for all examples
mdlinville Aug 25, 2025
6c5b58b
docs: use --quiet --upgrade flags for pip install to reduce output noise
mdlinville Aug 25, 2025
3bb8f89
docs: add reassuring note about import cell success
mdlinville Aug 25, 2025
6b42746
docs: add explanation about expected deprecation warning in Example 2
mdlinville Aug 25, 2025
59d90c7
docs: add reassuring note about import cell success
mdlinville Aug 25, 2025
c0bab04
docs: restore --quiet --upgrade flags to pip install command
mdlinville Aug 25, 2025
c1f8928
docs: remove --upgrade flag from pip install to avoid dependency conf…
mdlinville Aug 25, 2025
6b14efa
docs: remove notebook (moved to examples repo PR #609)
mdlinville Aug 25, 2025
394761f
fix: correct GITHUB_TOKEN parameter name in calibreapp image actions …
mdlinville Aug 25, 2025
b0eed78
Merge branch 'main' into DOCS-1016
mdlinville Aug 25, 2025
7508402
docs: address reviewer feedback - combine sections, fix terminology, …
mdlinville Aug 25, 2025
f6c3a69
docs: remove # characters from headings in reference documentation
mdlinville Aug 25, 2025
f5cfa3f
Apply suggestions from code review
mdlinville Aug 25, 2025
7b3b451
docs: remove remaining # characters from reference documentation head…
mdlinville Aug 25, 2025
dbd68d4
docs: remove # characters from English reference documentation headings
mdlinville Aug 25, 2025
4a45f33
docs: improve normalization table with matrix format for better clarity
mdlinville Aug 25, 2025
0bca154
docs: convert numbered normalization algorithm steps to bullet points…
mdlinville Aug 25, 2025
90d2d1d
docs: remove accidentally committed test notebook
mdlinville Aug 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion content/en/guides/models/track/log/media.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ with wandb.init(project="image-log-example") as run:
run.log({"examples": images})
```

We assume the image is gray scale if the last dimension is 1, RGB if it's 3, and RGBA if it's 4. If the array contains floats, we convert them to integers between `0` and `255`. If you want to normalize your images differently, you can specify the [`mode`](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes) manually or just supply a [`PIL.Image`](https://pillow.readthedocs.io/en/stable/reference/Image.html), as described in the "Logging PIL Images" tab of this panel.
The system assumes the image is gray scale if the last dimension is 1, RGB if it's 3, and RGBA if it's 4. If the array contains floats, the system automatically normalizes them to integers between `0` and `255`. For detailed information about normalization with PyTorch tensors and NumPy arrays, see the [Image normalization section]({{< relref "/ref/python/sdk/data-types/image.md#image-normalization" >}}) in the Image SDK reference. To normalize your images differently, you can specify the [`mode`](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes) manually or supply a [`PIL.Image`](https://pillow.readthedocs.io/en/stable/reference/Image.html), as described in the "Logging PIL Images" tab.
{{% /tab %}}
{{% tab header="Logging PIL Images" %}}
For full control over the conversion of arrays to images, construct the [`PIL.Image`](https://pillow.readthedocs.io/en/stable/reference/Image.html) yourself and provide it directly.
Expand Down
97 changes: 97 additions & 0 deletions content/en/ref/python/sdk/data-types/Image.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,103 @@ with wandb.init() as run:
run.log({"examples": examples})
```

## Image normalization
Comment thread
mdlinville marked this conversation as resolved.
Outdated

When you pass PyTorch tensors or NumPy arrays to `wandb.Image`, the pixel values are automatically normalized to the range [0, 255] unless you set `normalize=False`. This normalization is designed to handle and ensure proper display of common image formats.

### When normalization is applied

Normalization is applied to:
- **PyTorch tensors** (format: `(channel, height, width)`)
- **NumPy arrays** (format: `(height, width, channel)`)

Normalization is **NOT** applied to:
- **PIL Images** (passed as-is)
- **File paths** (loaded as-is)

### Normalization algorithm

The normalization algorithm automatically detects the input range and applies the appropriate transformation:

1. **If data is in range [0, 1]**: Values are multiplied by 255 and converted to uint8
```python
normalized_data = (data * 255).astype(np.uint8)
```

2. **If data is in range [-1, 1]**: Values are rescaled to [0, 255] using:
```python
normalized_data = (255 * 0.5 * (data + 1)).astype(np.uint8)
```

3. **For any other range**: Values are clipped to [0, 255] and converted to uint8
```python
normalized_data = data.clip(0, 255).astype(np.uint8)
```

### Examples of normalization effects

**Example 1: [0, 1] range data**
```python
import torch
import wandb

# Create tensor with values in [0, 1] range
tensor_0_1 = torch.rand(3, 64, 64) # Random values between 0 and 1

# This will multiply all values by 255
image = wandb.Image(tensor_0_1, caption="Normalized from [0,1] range")
```

**Example 2: [-1, 1] range data**
```python
import torch
import wandb

# Create tensor with values in [-1, 1] range
tensor_neg1_1 = torch.rand(3, 64, 64) * 2 - 1 # Random values between -1 and 1

# This will rescale: -1 → 0, 0 → 127.5, 1 → 255
image = wandb.Image(tensor_neg1_1, caption="Normalized from [-1,1] range")
```

**Example 3: Avoiding normalization with PIL Images**
```python
import torch
from PIL import Image as PILImage
import wandb

# Create tensor with values in [0, 1] range
tensor_0_1 = torch.rand(3, 64, 64)

# Convert to PIL Image to avoid normalization
pil_image = PILImage.fromarray((tensor_0_1.permute(1, 2, 0).numpy() * 255).astype('uint8'))
image = wandb.Image(pil_image, caption="No normalization applied")
```

**Example 4: Using normalize=False**
```python
import torch
import wandb

# Create tensor with values in [0, 1] range
tensor_0_1 = torch.rand(3, 64, 64)

# Disable normalization - values will be clipped to [0, 255]
image = wandb.Image(tensor_0_1, normalize=False, caption="Normalization disabled")
```

### Recommendations
Comment thread
mdlinville marked this conversation as resolved.
Outdated

1. **For consistent results**: Pre-process your data to the expected [0, 255] range before logging
2. **To avoid normalization**: Convert tensors to PIL Images using `PILImage.fromarray()`
3. **For debugging**: Use `normalize=False` to see the raw values (they will be clipped to [0, 255])
4. **For precise control**: Use PIL Images when you need exact pixel values

### Troubleshooting

- **Unexpected brightness**: If your tensor values are in [0, 1] range, they will be multiplied by 255, making the image much brighter
- **Data loss**: Values outside the [0, 255] range will be clipped, potentially losing information
- **Inconsistent behavior**: Different input types (tensor vs PIL vs file path) may produce different results

---

Expand Down
6 changes: 3 additions & 3 deletions content/ja/guides/models/track/log/media.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ images = wandb.Image(image_array, caption="Top: Output, Bottom: Input")
wandb.log({"examples": images})
```

最後の次元が1の場合はグレースケール、3の場合はRGB、4の場合はRGBAと仮定します。配列が浮動小数点数を含む場合、それらを`0`から`255`の整数に変換します。異なる方法で画像を正規化したい場合は、[`mode`](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes)を手動で指定するか、`"Logging PIL Images"`タブで説明されているように、単に[`PIL.Image`](https://pillow.readthedocs.io/en/stable/reference/Image.html)を提供することができます。
最後の次元が1の場合はグレースケール、3の場合はRGB、4の場合はRGBAと仮定します。配列が浮動小数点数を含む場合、正規化アルゴリズムを使用して自動的に`0`から`255`の整数に変換します。PyTorchテンソルとNumPy配列での正規化の動作についての詳細は、[Imageリファレンスの画像正規化セクション]({{< relref path="/ref/python/data-types/image.md#image-normalization" lang="ja" >}})を参照してください。異なる方法で画像を正規화したい場合は、[`mode`](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes)を手動で指定するか、`"Logging PIL Images"`タブで説明されているように、単に[`PIL.Image`](https://pillow.readthedocs.io/en/stable/reference/Image.html)を提供することができます。
{{% /tab %}}
{{% tab header="PIL Imagesをログする" %}}
配列から画像への変換を完全に制御するために、[`PIL.Image`](https://pillow.readthedocs.io/en/stable/reference/Image.html)を自分で構築し、直接提供してください。
Expand Down Expand Up @@ -564,7 +564,7 @@ runが終了すると、UIで分子の3D可視化と対話できるようにな

### PNG 画像

[`wandb.Image`]({{< relref path="/ref/python/data-types/image.md" lang="ja" >}})は`numpy`配列や`PILImage`のインスタンスをデフォルトでPNGに変換します。
`wandb.Image`は`numpy`配列や`PILImage`のインスタンスをデフォルトでPNGに変換します。

```python
wandb.log({"example": wandb.Image(...)})
Expand All @@ -584,7 +584,7 @@ wandb.log({"example": wandb.Video("myvideo.mp4")})

## 分子の2Dビュー

[`wandb.Image`]({{< relref path="/ref/python/data-types/image.md" lang="ja" >}})データ型と[`rdkit`](https://www.rdkit.org/docs/index.html)を使用して分子の2Dビューをログできます:
[`wandb.Image`]({{< relref "/ref/python/data-types/image.md" >}})データ型と[`rdkit`](https://www.rdkit.org/docs/index.html)を使用して分子の2Dビューをログできます:

```python
molecule = rdkit.Chem.MolFromSmiles("CC(=O)O")
Expand Down
98 changes: 98 additions & 0 deletions content/ja/ref/python/data-types/image.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,104 @@ Image(

注意 : `wandb.Image` として `torch.Tensor` をログする際、画像は正規化されます。画像を正規化したくない場合は、テンソルを PIL Image に変換してください。

## 画像の正規化

PyTorch テンソルや NumPy 配列を `wandb.Image` に渡すと、`normalize=False` を設定しない限り、ピクセル値は自動的に [0, 255] の範囲に正規化されます。この正規化は、一般的な画像データ形式を処理し、適切な表示を確保するために設計されています。

### 正規化が適用される場合

正規化は以下に適用されます:
- **PyTorch テンソル** (形式: `(channel, height, width)`)
- **NumPy 配列** (形式: `(height, width, channel)`)

正規化は以下には適用されません:
- **PIL 画像** (そのまま渡される)
- **ファイルパス** (そのまま読み込まれる)

### 正規化アルゴリズム

正規化アルゴリズムは入力範囲を自動的に検出し、適切な変換を適用します:

1. **データが [0, 1] の範囲にある場合**: 値に255を掛けてuint8に変換
```python
normalized_data = (data * 255).astype(np.uint8)
```

2. **データが [-1, 1] の範囲にある場合**: 以下の式で [0, 255] にリスケール
```python
normalized_data = (255 * 0.5 * (data + 1)).astype(np.uint8)
```

3. **その他の範囲の場合**: 値を [0, 255] にクリップしてuint8に変換
```python
normalized_data = data.clip(0, 255).astype(np.uint8)
```

### 正規化効果の例

**例1: [0, 1] 範囲のデータ**
```python
import torch
import wandb

# [0, 1] 範囲の値を持つテンソルを作成
tensor_0_1 = torch.rand(3, 64, 64) # 0から1の間のランダム値

# すべての値に255を掛ける
image = wandb.Image(tensor_0_1, caption="[0,1]範囲から正規化")
```

**例2: [-1, 1] 範囲のデータ**
```python
import torch
import wandb

# [-1, 1] 範囲の値を持つテンソルを作成
tensor_neg1_1 = torch.rand(3, 64, 64) * 2 - 1 # -1から1の間のランダム値

# リスケール: -1 → 0, 0 → 127.5, 1 → 255
image = wandb.Image(tensor_neg1_1, caption="[-1,1]範囲から正規化")
```

**例3: PIL画像で正規化を回避**
```python
import torch
from PIL import Image as PILImage
import wandb

# [0, 1] 範囲の値を持つテンソルを作成
tensor_0_1 = torch.rand(3, 64, 64)

# PIL画像に変換して正規化を回避
pil_image = PILImage.fromarray((tensor_0_1.permute(1, 2, 0).numpy() * 255).astype('uint8'))
image = wandb.Image(pil_image, caption="正規化は適用されません")
```

**例4: normalize=False を使用**
```python
import torch
import wandb

# [0, 1] 範囲の値を持つテンソルを作成
tensor_0_1 = torch.rand(3, 64, 64)

# 正規化を無効化 - 値は [0, 255] にクリップされる
image = wandb.Image(tensor_0_1, normalize=False, caption="正規化無効")
```

### ベストプラクティス

1. **一貫した結果のため**: ログする前にデータを期待される [0, 255] 範囲に前処理する
2. **正規化を回避するため**: `PILImage.fromarray()` を使用してテンソルをPIL画像に変換する
3. **デバッグのため**: `normalize=False` を使用して生の値を確認する([0, 255] にクリップされる)
4. **精密な制御のため**: 正確なピクセル値が必要な場合はPIL画像を使用する

### よくある落とし穴

- **予期しない明度**: テンソル値が [0, 1] 範囲にある場合、255倍されるため画像が非常に明るくなる
- **データ損失**: [0, 255] 範囲外の値はクリップされ、情報が失われる可能性がある
- **一貫性のない動作**: 異なる入力タイプ(テンソル vs PIL vs ファイルパス)で異なる結果が得られる可能性がある

#### 例:

### numpy 配列から wandb.Image を作成
Expand Down
6 changes: 3 additions & 3 deletions content/ko/guides/models/track/log/media.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ images = wandb.Image(image_array, caption="Top: Output, Bottom: Input")
wandb.log({"examples": images})
```

마지막 차원이 1이면 이미지가 회색조, 3이면 RGB, 4이면 RGBA라고 가정합니다. 배열에 float가 포함된 경우 `0`과 `255` 사이의 정수로 변환합니다. 이미지를 다르게 정규화하려면 [`mode`](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes)를 수동으로 지정하거나 이 패널의 "PIL 이미지 로깅" 탭에 설명된 대로 [`PIL.Image`](https://pillow.readthedocs.io/en/stable/reference/Image.html)를 제공하면 됩니다.
마지막 차원이 1이면 이미지가 회색조, 3이면 RGB, 4이면 RGBA라고 가정합니다. 배열에 float가 포함된 경우 정규화 알고리즘을 사용하여 자동으로 `0`과 `255` 사이의 정수로 변환합니다. PyTorch 텐서와 NumPy 배열에서 정규화가 어떻게 작동하는지에 대한 자세한 정보는 Image 참조 문서를 참조하세요. 이미지를 다르게 정규화하려면 [`mode`](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes)를 수동으로 지정하거나 이 패널의 "PIL 이미지 로깅" 탭에 설명된 대로 [`PIL.Image`](https://pillow.readthedocs.io/en/stable/reference/Image.html)를 제공하면 됩니다.
{{% /tab %}}
{{% tab header="PIL 이미지 로깅" %}}
배열을 이미지로 변환하는 것을 완벽하게 제어하려면 [`PIL.Image`](https://pillow.readthedocs.io/en/stable/reference/Image.html)를 직접 구성하여 제공합니다.
Expand Down Expand Up @@ -557,7 +557,7 @@ run이 완료되면 UI에서 분자의 3D 시각화와 상호 작용할 수 있

### PNG 이미지

[`wandb.Image`]({{< relref path="/ref/python/data-types/image.md" lang="ko" >}})는 `numpy` 배열 또는 `PILImage` 인스턴스를 기본적으로 PNG로 변환합니다.
`wandb.Image`는 `numpy` 배열 또는 `PILImage` 인스턴스를 기본적으로 PNG로 변환합니다.

```python
wandb.log({"example": wandb.Image(...)})
Expand All @@ -577,7 +577,7 @@ wandb.log({"example": wandb.Video("myvideo.mp4")})

## 분자의 2D 보기

[`wandb.Image`]({{< relref path="/ref/python/data-types/image.md" lang="ko" >}}) 데이터 유형과 [`rdkit`](https://www.rdkit.org/docs/index.html)을 사용하여 분자의 2D 보기를 기록할 수 있습니다.
[`wandb.Image`]({{< relref "/ref/python/data-types/image.md" >}}) 데이터 유형과 [`rdkit`](https://www.rdkit.org/docs/index.html)을 사용하여 분자의 2D 보기를 기록할 수 있습니다.

```python
molecule = rdkit.Chem.MolFromSmiles("CC(=O)O")
Expand Down
98 changes: 98 additions & 0 deletions content/ko/ref/python/data-types/image.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,104 @@ Image(

참고 : `torch.Tensor`를 `wandb.Image`로 로깅할 때 이미지는 정규화됩니다. 이미지를 정규화하지 않으려면 텐서를 PIL Image로 변환하십시오.

## 이미지 정규화

PyTorch 텐서나 NumPy 배열을 `wandb.Image`에 전달하면 `normalize=False`를 설정하지 않는 한 픽셀 값이 자동으로 [0, 255] 범위로 정규화됩니다. 이 정규화는 일반적인 이미지 데이터 형식을 처리하고 적절한 표시를 보장하기 위해 설계되었습니다.

### 정규화가 적용되는 경우

정규화는 다음에 적용됩니다:
- **PyTorch 텐서** (형식: `(channel, height, width)`)
- **NumPy 배열** (형식: `(height, width, channel)`)

정규화는 다음에 적용되지 않습니다:
- **PIL 이미지** (그대로 전달됨)
- **파일 경로** (그대로 로드됨)

### 정규화 알고리즘

정규화 알고리즘은 입력 범위를 자동으로 감지하고 적절한 변환을 적용합니다:

1. **데이터가 [0, 1] 범위에 있는 경우**: 값에 255를 곱하고 uint8로 변환
```python
normalized_data = (data * 255).astype(np.uint8)
```

2. **데이터가 [-1, 1] 범위에 있는 경우**: 다음 공식으로 [0, 255]에 리스케일
```python
normalized_data = (255 * 0.5 * (data + 1)).astype(np.uint8)
```

3. **기타 범위의 경우**: 값을 [0, 255]로 클립하고 uint8로 변환
```python
normalized_data = data.clip(0, 255).astype(np.uint8)
```

### 정규화 효과 예시

**예시 1: [0, 1] 범위 데이터**
```python
import torch
import wandb

# [0, 1] 범위의 값을 가진 텐서 생성
tensor_0_1 = torch.rand(3, 64, 64) # 0에서 1 사이의 랜덤 값

# 모든 값에 255를 곱함
image = wandb.Image(tensor_0_1, caption="[0,1] 범위에서 정규화")
```

**예시 2: [-1, 1] 범위 데이터**
```python
import torch
import wandb

# [-1, 1] 범위의 값을 가진 텐서 생성
tensor_neg1_1 = torch.rand(3, 64, 64) * 2 - 1 # -1에서 1 사이의 랜덤 값

# 리스케일: -1 → 0, 0 → 127.5, 1 → 255
image = wandb.Image(tensor_neg1_1, caption="[-1,1] 범위에서 정규화")
```

**예시 3: PIL 이미지로 정규화 회피**
```python
import torch
from PIL import Image as PILImage
import wandb

# [0, 1] 범위의 값을 가진 텐서 생성
tensor_0_1 = torch.rand(3, 64, 64)

# PIL 이미지로 변환하여 정규화 회피
pil_image = PILImage.fromarray((tensor_0_1.permute(1, 2, 0).numpy() * 255).astype('uint8'))
image = wandb.Image(pil_image, caption="정규화가 적용되지 않음")
```

**예시 4: normalize=False 사용**
```python
import torch
import wandb

# [0, 1] 범위의 값을 가진 텐서 생성
tensor_0_1 = torch.rand(3, 64, 64)

# 정규화 비활성화 - 값은 [0, 255]로 클립됨
image = wandb.Image(tensor_0_1, normalize=False, caption="정규화 비활성화")
```

### 모범 사례

1. **일관된 결과를 위해**: 로깅하기 전에 데이터를 예상되는 [0, 255] 범위로 전처리
2. **정규화를 회피하기 위해**: `PILImage.fromarray()`를 사용하여 텐서를 PIL 이미지로 변환
3. **디버깅을 위해**: `normalize=False`를 사용하여 원시 값 확인 ([0, 255]로 클립됨)
4. **정밀한 제어를 위해**: 정확한 픽셀 값이 필요한 경우 PIL 이미지 사용

### 일반적인 함정

- **예상치 못한 밝기**: 텐서 값이 [0, 1] 범위에 있으면 255배되어 이미지가 매우 밝아짐
- **데이터 손실**: [0, 255] 범위를 벗어나는 값은 클립되어 정보가 손실될 수 있음
- **일관성 없는 동작**: 다른 입력 유형(텐서 vs PIL vs 파일 경로)에서 다른 결과가 나올 수 있음

#### 예시:

### numpy array에서 wandb.Image 생성
Expand Down