Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add image_matrix #909

Merged
merged 5 commits into from
Jan 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
70 changes: 70 additions & 0 deletions docs/components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,76 @@ visualdl --logdir ./log --port 8080
<img src="https://visualdl.bj.bcebos.com/images/image-eye.gif" width="60%"/>
</p>

### 添加图片矩阵
除使用add_image记录一张图片之外,还可以使用add_image_matrix一次添加多张图片并生成一张图片矩阵,接口及参数说明如下:
add_image_matrix的记录接口如下:

```python
add_image_matrix(tag, imgs, step, rows=-1, scale=1, walltime=None, dataformats="HWC")
```
接口参数说明如下:
| 参数 | 格式 | 含义 |
| -------- | ------------- | ------------------------------------------- |
| tag | string | 记录指标的标志,如`train/loss`,不能含有`%` |
| imgs | numpy.ndarray | 以ndarray格式表示的多张图片,第一维为图片的数量 |
| step | int | 记录的步数 |
| rows | int | 生成图片矩阵的行数,默认值为-1,表示尽量把传入的图片组合成行列数相近的形式 |
| scale | int | 图片放大比例,默认为1 |
| walltime | int | 记录数据的时间戳,默认为当前时间戳 |
| dataformats| string | 传入的图片格式,包括`NCHW`、`HWC`、`HW`,默认为`HWC`|

#### Demo
下面展示了使用 Image 组件合成并记录多张图片数据的示例,代码文件请见[Image组件](https://github.com/PaddlePaddle/VisualDL/blob/develop/demo/components/image_matrix_test.py)
```python
import numpy as np
from PIL import Image
from visualdl import LogWriter


def random_crop(img):
"""Get random block of img, which size is 100x100.
"""
img = Image.open(img)
w, h = img.size
random_w = np.random.randint(0, w - 100)
random_h = np.random.randint(0, h - 100)
r = img.crop((random_w, random_h, random_w + 100, random_h + 100))
return np.asarray(r)


if __name__ == '__main__':
imgs = []
# 获取8张图像
for step in range(8):
img = random_crop("../../docs/images/dog.jpg")
imgs.append(img)
imgs = np.array(imgs)

with LogWriter(logdir='./log/image_matrix_test/train') as writer:
# 合成长宽尽量接近的图形矩阵,本例生成3X3的矩阵
writer.add_image_matrix(tag='test_images', step=1, imgs=imgs, rows=-1)
# 合成长为1的图形矩阵,本例生成1x8的矩阵
writer.add_image_matrix(tag='test_images', step=2, imgs=imgs, rows=1)
# 合成长为2的图形矩阵,本例生成2X4的矩阵
writer.add_image_matrix(tag='test_images', step=3, imgs=imgs, rows=2)
# 合成长为3的图形矩阵,本例生成3X3的矩阵
writer.add_image_matrix(tag='test_images', step=4, imgs=imgs, rows=3)
# 合成长为4的图形矩阵,本例生成4X2的矩阵
writer.add_image_matrix(tag='test_images', step=5, imgs=imgs, rows=4)
```
运行上述程序后,在命令行执行
```shell
visualdl --logdir ./log --port 8080
```

在浏览器输入`http://127.0.0.1:8080`,即可查看图片数据。

<p align="center">
<img src="https://user-images.githubusercontent.com/28444161/104555348-ad63df80-5678-11eb-9d68-04f7f7451eac.png" width="40%"/>
<img src="https://user-images.githubusercontent.com/28444161/104556243-2dd71000-567a-11eb-9222-225b0acdf56b.png" width="40%"/>
</p>


## Audio--音频播放组件

### 介绍
Expand Down
89 changes: 89 additions & 0 deletions visualdl/utils/img_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Copyright (c) 2020 VisualDL Authors. All Rights Reserve.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# =======================================================================
import math
from functools import reduce

import numpy as np

from visualdl.component.base_component import convert_to_HWC


def padding_image(img, height, width):
height_old, width_old, _ = img.shape
height_before = math.floor((height - height_old) / 2)
height_after = height - height_old - height_before

width_before = math.floor((width - width_old) / 2)
width_after = width - width_old - width_before

return np.pad(img, ((height_before, height_after), (width_before, width_after), (0, 0)))


def merge_images(imgs, dataformats, scale=1.0, rows=-1):
assert rows <= len(imgs), "rows should not greater than numbers of pictures"
channel = imgs[0].shape[2]
# convert format of each image to `hwc`
for i, img in enumerate(imgs):
imgs[i] = convert_to_HWC(img, dataformats)

height = -1
width = -1

for img in imgs:
height = height if height > img.shape[0] else img.shape[0]
width = width if width > img.shape[1] else img.shape[1]

# padding every sub-image with height and width
for i, img in enumerate(imgs):
imgs[i] = padding_image(img, height, width)

# get row and col
len_imgs = len(imgs)
if -1 == rows:
rows = cols = math.floor(math.sqrt(len_imgs))
while rows*cols < len_imgs:
if rows <= cols:
rows += 1
else:
cols += 1
else:
cols = math.ceil(len_imgs/rows)

# add white sub-image
for i in range(rows*cols-len_imgs):
imgs = np.concatenate((imgs, np.zeros((height, width, channel), dtype=np.uint8)[None, :]))

imgs = reduce(lambda x, y: np.concatenate((x, y)), [
reduce(lambda x, y: np.concatenate((x, y), 1),
imgs[i * cols: (i + 1) * cols]) for i in range(rows)])

# choose bigger number of rows and cols

scale = 1.0/scale * rows if rows > cols else 1.0/scale * cols

dsize = tuple(map(lambda x: math.floor(x/scale), imgs.shape))[-2::-1]

try:
import cv2

imgs = cv2.resize(src=imgs, dsize=dsize)
except ImportError:
from PIL import Image

imgs = Image.fromarray(imgs)
imgs.resize(dsize)
imgs = np.array(imgs)

return imgs
31 changes: 31 additions & 0 deletions visualdl/writer/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import time
import numpy as np
from visualdl.writer.record_writer import RecordFileWriter
from visualdl.utils.img_util import merge_images
from visualdl.component.base_component import scalar, image, embedding, audio, histogram, pr_curve, roc_curve, meta_data


Expand Down Expand Up @@ -189,6 +190,36 @@ def add_image(self, tag, img, step, walltime=None, dataformats="HWC"):
image(tag=tag, image_array=img, step=step, walltime=walltime,
dataformats=dataformats))

def add_image_matrix(self, tag, imgs, step, rows=-1, scale=1.0, walltime=None, dataformats="HWC"):
"""Add an image to vdl record file.

Args:
tag (string): Data identifier
imgs (np.ndarray): Image represented by a numpy.array
step (int): Step of image
rows (int): Number of rows, -1 means as close as possible to the square
scale (float): Image zoom scale
walltime (int): Wall time of image
dataformats (string): Format of image

Example:
from PIL import Image
import numpy as np

I = Image.open("./test.png")
I_array = np.array([I, I, I])
writer.add_image_matrix(tag="lll", imgs=I_array, step=0)
"""
if '%' in tag:
raise RuntimeError("% can't appear in tag!")
walltime = round(time.time() * 1000) if walltime is None else walltime
img = merge_images(imgs=imgs, dataformats=dataformats, scale=scale, rows=rows)
self.add_image(tag=tag,
img=img,
step=step,
walltime=walltime,
dataformats=dataformats)

def add_embeddings(self, tag, labels, hot_vectors, labels_meta=None, walltime=None):
"""Add embeddings to vdl record file.

Expand Down