Skip to content

Commit

Permalink
Merge pull request #22 from LeoHsiao1/dev
Browse files Browse the repository at this point in the history
merge dev to master
  • Loading branch information
LeoHsiao1 authored Jun 8, 2020
2 parents 36b05ac + ef67b6b commit ef8fe8d
Show file tree
Hide file tree
Showing 32 changed files with 655 additions and 320 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-18.04, windows-2019]
os: [ubuntu-18.04, macOS-latest, windows-2019]
python-version: [3.5, 3.6, 3.7, 3.8]
steps:
- name: git pull
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ Read/Write metadata of digital image, including [EXIF](https://en.wikipedia.org/
## Features

- Base on C++ API of [Exiv2](https://www.exiv2.org/index.html) and [pybind11](https://github.com/pybind/pybind11).
- Supports running on Linux and Windows, with Python3(64bit, including `3.5` `3.6` `3.7` `3.8`).
- Supports running on Linux, MacOS and Windows, with Python3(64bit, including `3.5` `3.6` `3.7` `3.8`).
- [Supports various metadata](https://www.exiv2.org/metadata.html)
- [Supports various image formats](https://dev.exiv2.org/projects/exiv2/wiki/Supported_image_formats)
- Supports opening images based on the file path or from bytes data.
- Supports Unicode characters that contained in image path or metadata.

## Defects

- Only images smaller than 2GB can be opened.
- Not thread safe.

## Usage

- [Tutorial](https://github.com/LeoHsiao1/pyexiv2/blob/master/docs/Tutorial.md)
Expand Down
80 changes: 57 additions & 23 deletions docs/Tutorial-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

## API列表

请调用`pyexiv2.Image`类的公有方法:
```python
class Image(filename, encoding='utf-8')
```py
class Image:
def __init__(self, filename, encoding='utf-8')
def read_exif(self, encoding='utf-8') -> dict
def read_iptc(self, encoding='utf-8') -> dict
def read_xmp(self, encoding='utf-8') -> dict
Expand All @@ -22,12 +22,34 @@ class Image(filename, encoding='utf-8')
def clear_xmp(self)

def close(self)

class ImageData(Image):
def __init__(self, data: bytes)
def get_bytes(self) -> bytes

set_log_level()
```

## 类 Image

-`Image` 用于根据文件路径打开图片。例如:
```py
>>> from pyexiv2 import Image
>>> img = Image(r'.\pyexiv2\tests\1.jpg')
>>> data = img.read_exif()
>>> img.close()
```
- 当你处理完图片之后,请记得调用 `img.close()` ,以释放用于存储图片数据的内存。不调用该方法会导致内存泄漏,但不会锁定文件描述符。
- 通过 `with` 关键字打开图片时,它会自动关闭图片。例如:
```py
with Image(r'.\pyexiv2\tests\1.jpg') as img:
ims.read_exif()
```

### Image.read_*()

- 示例:
```python
```py
>>> from pyexiv2 import Image
>>> img = Image(r'.\pyexiv2\tests\1.jpg')
>>> img.read_exif()
Expand All @@ -47,14 +69,13 @@ class Image(filename, encoding='utf-8')
```
另一个例子:中国地区的 Windows 电脑通常用 GBK 编码文件路径,因此它们不能被 utf-8 解码。
- 使用`Image.read_*()`是安全的。这些方法永远不会影响图片文件。(md5不变)
- 如果 XMP 元数据包含 '\v''\f',它将被空格 ' ' 代替。
- 元数据的读取速度与元数据的数量成反比,不管图像的大小如何。

- 如果 XMP 元数据包含 `\v` 或 `\f`,它将被空格 ` ` 代替。
- 元数据的读取速度与元数据的数量成反比,不管图片的大小如何。

### Image.modify_*()

- 示例:
```python
```py
>>> img = Image(r'.\pyexiv2\tests\1.jpg')
>>> # 准备要修改的XMP数据
>>> dict1 = {'Xmp.xmp.CreateDate': '2019-06-23T19:45:17.834', # 这将覆盖该标签的原始值,如果不存在该标签则将其添加
Expand All @@ -70,20 +91,20 @@ class Image(filename, encoding='utf-8')
```
- 以同样的方式使用 `img.modify_exif()``img.modify_iptc()`
- 如果你尝试修改一个非标准的标签,则可能引发一个异常。如下:
```python
```py
>>> img.modify_exif({'Exif.Image.mytag001': 'test'}) # 失败
RuntimeError: Invalid tag name or ifdId `mytag001', ifdId 1
>>> img.modify_xmp({'Xmp.dc.mytag001': 'test'}) # 成功
>>> img.read_xmp()['Xmp.dc.mytag001']
'test'
```
- 某些特殊的标签不能被 pyexiv2 修改。例如:
```python
```py
>>> img.modify_exif({'Exif.Photo.MakerNote': 'test,,,'})
>>> img.read_exif()['Exif.Photo.MakerNote']
''
''
```
```python
```py
>>> img.read_xmp()['Xmp.xmpMM.History']
'type="Seq"'
>>> img.modify_xmp({'Xmp.xmpMM.History': 'type="Seq"'})
Expand All @@ -92,32 +113,45 @@ class Image(filename, encoding='utf-8')
```
- 修改元数据的速度与图片的大小成反比。


### Image.clear_*()

- 调用 `img.clear_exif()` 将删除图片的所有 EXIF 元数据。一旦清除元数据,pyexiv2 可能无法完全恢复它。
- 以同样的方式使用 `img.clear_iptc()``img.clear_xmp()` .
- 按类似的方式使用 `img.clear_iptc()``img.clear_xmp()` .

### Image.close()
## 类 ImageData

- 当你处理完图片之后,请记得调用 `img.close()` ,以释放用于存储图像数据的内存。不调用该方法会导致内存泄漏,但不会锁定文件描述符。
- 通过 `with` 关键字打开图片时,它会自动关闭图片。例如:
```python
with Image(r'.\pyexiv2\tests\1.jpg') as img:
ims.read_exif()
-`ImageData`, 继承于类 `Image`, 用于从字节数据中打开图片。
- 读取的示例:
```py
with open(r'.\pyexiv2\tests\1.jpg', 'rb') as f:
with ImageData(f.read()) as img:
data = img.read_exif()
```
- 修改的示例:
```py
with open(r'.\pyexiv2\tests\1.jpg', 'rb+') as f:
with ImageData(f.read()) as img:
changes = {'Iptc.Application2.ObjectName': 'test'}
img.modify_iptc(changes)
f.seek(0)
# 获取图片的字节数据并保存到文件中
f.write(img.get_bytes())
f.seek(0)
with ImageData(f.read()) as img:
result = img.read_iptc()
```

## 数据类型

- 图片元数据的值可能是 Short、Long、byte、Ascii 等类型。读取时,大多数将被 pyexiv2 转换为 String 类型。
- 某些元数据是字符串列表。例如:
```python
```py
>>> img.modify_xmp({'Xmp.dc.subject': ['tag1', 'tag2', 'tag3']})
>>> img.read_xmp()['Xmp.dc.subject']
['tag1', 'tag2', 'tag3']
```
如果字符串中包含 `', '` ,它会被分割。如下:
```python
```py
>>> img.modify_xmp({'Xmp.dc.subject': 'tag1,tag2, tag3'})
>>> img.read_xmp()['Xmp.dc.subject']
['tag1,tag2', 'tag3']
Expand All @@ -136,7 +170,7 @@ class Image(filename, encoding='utf-8')
- 默认的日志级别是 `warn` ,因此更低级别的日志不会被处理。
- `error` 日志会被转换成异常并抛出,其它日志则会被打印到 stdout 。
- 调用函数 `pyexiv2.set_log_level()` 可以设置处理日志的级别。例如:
```python
```py
>>> import pyexiv2
>>> img = pyexiv2.Image(r'.\pyexiv2\tests\1.jpg')
>>> img.modify_xmp({'Xmp.xmpMM.History': 'type="Seq"'})
Expand Down
77 changes: 56 additions & 21 deletions docs/Tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

## API list

Please call the public methods of class `pyexiv2.Image`:
```python
class Image(filename, encoding='utf-8')
```py
class Image:
def __init__(self, filename, encoding='utf-8')
def read_exif(self, encoding='utf-8') -> dict
def read_iptc(self, encoding='utf-8') -> dict
def read_xmp(self, encoding='utf-8') -> dict
Expand All @@ -22,13 +22,34 @@ class Image(filename, encoding='utf-8')
def clear_xmp(self)

def close(self)

class ImageData(Image):
def __init__(self, data: bytes)
def get_bytes(self) -> bytes

set_log_level()
```

## class Image

- Class `Image` is used to open an image based on the file path. For example:
```py
>>> from pyexiv2 import Image
>>> img = Image(r'.\pyexiv2\tests\1.jpg')
>>> data = img.read_exif()
>>> img.close()
```
- When you're done with the image, remember to call `img.close()` to free the memory for storing image data. Not calling this method causes a memory leak, but it doesn't lock the file descriptor.
- Opening an image by keyword `with` will close the image automatically. For example:
```py
with Image(r'.\pyexiv2\tests\1.jpg') as img:
data = ims.read_exif()
```

### Image.read_*()

- Sample:
```python
>>> from pyexiv2 import Image
```py
>>> img = Image(r'.\pyexiv2\tests\1.jpg')
>>> img.read_exif()
{'Exif.Image.DateTime': '2019:06:23 19:45:17', 'Exif.Image.Artist': 'TEST', 'Exif.Image.Rating': '4', ...}
Expand All @@ -47,13 +68,13 @@ class Image(filename, encoding='utf-8')
```
Another example: Windows computers in China usually encoded file paths by GBK, so they cannot be decoded by utf-8.
- It is safe to use `Image.read_*()`. These methods never affect image files. (md5 unchanged)
- If the XMP metadata contains '\v' or '\f', it will be replaced with space ' '.
- If the XMP metadata contains `\v` or `\f`, it will be replaced with space ` `.
- The speed of reading metadata is inversely proportional to the amount of metadata, regardless of the size of the image.
### Image.modify_*()

- Sample:
```python
```py
>>> img = Image(r'.\pyexiv2\tests\1.jpg')
>>> # Prepare the XMP data you want to modify
>>> dict1 = {'Xmp.xmp.CreateDate': '2019-06-23T19:45:17.834', # This will overwrite its original value, or add it if it doesn't exist
Expand All @@ -69,20 +90,20 @@ class Image(filename, encoding='utf-8')
```
- Use `img.modify_exif()` and `img.modify_iptc()` in the same way.
- If you try to modify a non-standard tag, you may cause an exception. Such as below:
```python
```py
>>> img.modify_exif({'Exif.Image.mytag001': 'test'}) # Failed
RuntimeError: Invalid tag name or ifdId `mytag001', ifdId 1
>>> img.modify_xmp({'Xmp.dc.mytag001': 'test'}) # Successful
>>> img.read_xmp()['Xmp.dc.mytag001']
'test'
```
- Some special tags cannot be modified by pyexiv2. For example:
```python
```py
>>> img.modify_exif({'Exif.Photo.MakerNote': 'test,,,'})
>>> img.read_exif()['Exif.Photo.MakerNote']
''
''
```
```python
```py
>>> img.read_xmp()['Xmp.xmpMM.History']
'type="Seq"'
>>> img.modify_xmp({'Xmp.xmpMM.History': 'type="Seq"'})
Expand All @@ -94,28 +115,42 @@ class Image(filename, encoding='utf-8')
### Image.clear_*()

- Calling `img.clear_exif()` will delete all EXIF metadata of the image. Once cleared, pyexiv2 may not be able to recover it completely.
- Use `img.clear_iptc()` and `img.clear_xmp()` in the same way.
- Use `img.clear_iptc()` and `img.clear_xmp()` in the similar way.

### Image.close()
## class ImageData

- When you're done with the image, remember to call `img.close()` to free the memory for storing image data. Not calling this method causes a memory leak, but it doesn't lock the file descriptor.
- Opening an image by `with` keyword will close the image automatically. For example:
```python
with Image(r'.\pyexiv2\tests\1.jpg') as img:
ims.read_exif()
- Class `ImageData`, inherited from class `Image`, is used to open an image from bytes data.
- Example of reading:
```py
with open(r'.\pyexiv2\tests\1.jpg', 'rb') as f:
with ImageData(f.read()) as img:
data = img.read_exif()
```
- Example of modifing:
```py
with open(r'.\pyexiv2\tests\1.jpg', 'rb+') as f:
with ImageData(f.read()) as img:
changes = {'Iptc.Application2.ObjectName': 'test'}
img.modify_iptc(changes)
f.seek(0)
# Get the bytes data of the image and save it to the file
f.write(img.get_bytes())
f.seek(0)
with ImageData(f.read()) as img:
result = img.read_iptc()
```

## Data types

- The value of the image metadata might be of type Short, Long, byte, Ascii, and so on. Most of them will be converted to String type by pyexiv2 when reading.
- Some metadata is a list of strings. For example:
```python
```py
>>> img.modify_xmp({'Xmp.dc.subject': ['tag1', 'tag2', 'tag3']})
>>> img.read_xmp()['Xmp.dc.subject']
['tag1', 'tag2', 'tag3']
```
If the string contains `', '` , it will be split. For example:
```python
```py
>>> img.modify_xmp({'Xmp.dc.subject': 'tag1,tag2, tag3'})
>>> img.read_xmp()['Xmp.dc.subject']
['tag1,tag2', 'tag3']
Expand All @@ -134,7 +169,7 @@ class Image(filename, encoding='utf-8')
- The default log level is `warn`, so that the lower logs will not be handled.
- The `error` log will be converted to an exception and thrown. Other logs will be printed to stdout.
- Call the function `pyexiv2.set_log_level()` to set the level of handling logs. For example:
```python
```py
>>> import pyexiv2
>>> img = pyexiv2.Image(r'.\pyexiv2\tests\1.jpg')
>>> img.modify_xmp({'Xmp.xmpMM.History': 'type="Seq"'})
Expand Down
Loading

0 comments on commit ef8fe8d

Please sign in to comment.