anshitsu 0.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anshitsu-0.0.0/LICENSE +21 -0
- anshitsu-0.0.0/PKG-INFO +183 -0
- anshitsu-0.0.0/README.md +161 -0
- anshitsu-0.0.0/pyproject.toml +50 -0
- anshitsu-0.0.0/src/anshitsu/__init__.py +0 -0
- anshitsu-0.0.0/src/anshitsu/__version__.py +1 -0
- anshitsu-0.0.0/src/anshitsu/cli.py +14 -0
- anshitsu-0.0.0/src/anshitsu/process.py +139 -0
- anshitsu-0.0.0/src/anshitsu/retouch.py +236 -0
anshitsu-0.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Iosif Takakura
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
anshitsu-0.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: anshitsu
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: A tiny digital photographic utility.
|
|
5
|
+
Home-page: https://github.com/huideyeren
|
|
6
|
+
License: MIT
|
|
7
|
+
Author: Iosif Takakura
|
|
8
|
+
Author-email: iosif@huideyeren.info
|
|
9
|
+
Requires-Python: >=3.10,<3.13
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Requires-Dist: Pillow (>=10.1.0,<11.0.0)
|
|
15
|
+
Requires-Dist: colorcorrect (>=0.9.1,<0.10.0)
|
|
16
|
+
Requires-Dist: fire (>=0.5.0,<0.6.0)
|
|
17
|
+
Requires-Dist: numpy (>=1.26.0,<2.0.0)
|
|
18
|
+
Project-URL: Documentation, https://github.com/huideyeren/anshitsu
|
|
19
|
+
Project-URL: Repository, https://github.com/huideyeren/anshitsu
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# Anshitsu
|
|
23
|
+
|
|
24
|
+
[](https://github.com/huideyeren/anshitsu/actions/workflows/testing.yml)
|
|
25
|
+
|
|
26
|
+
[](https://codecov.io/gh/huideyeren/anshitsu)
|
|
27
|
+
|
|
28
|
+
A tiny digital photographic utility.
|
|
29
|
+
|
|
30
|
+
"Anshitsu" means a darkroom in Japanese.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
Run this command in an environment where Python 3.10 or higher is installed.
|
|
35
|
+
|
|
36
|
+
We have tested it on Windows, Mac, and Ubuntu on GitHub Actions, but we have not tested it on Macs with Apple Silicon, so please use it at your own risk on Macs with Apple Silicon.
|
|
37
|
+
|
|
38
|
+
``` shell
|
|
39
|
+
pip install anshitsu
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
It is as described in the following help.
|
|
45
|
+
|
|
46
|
+
``` shell
|
|
47
|
+
INFO: Showing help with the command 'anshitsu -- --help'.
|
|
48
|
+
|
|
49
|
+
NAME
|
|
50
|
+
anshitsu - Process Runnner for Command Line Interface
|
|
51
|
+
|
|
52
|
+
SYNOPSIS
|
|
53
|
+
anshitsu PATH <flags>
|
|
54
|
+
|
|
55
|
+
DESCRIPTION
|
|
56
|
+
This utility converts the colors of images such as photos.
|
|
57
|
+
|
|
58
|
+
If you specify a directory path, it will convert
|
|
59
|
+
the image files in the specified directory.
|
|
60
|
+
If you specify a file path, it will convert the specified file.
|
|
61
|
+
If you specify an option, the specified conversion will be performed.
|
|
62
|
+
|
|
63
|
+
Tosaka mode is a mode that expresses the preference of
|
|
64
|
+
Tosaka-senpai, a character in "Kyūkyoku Chōjin R",
|
|
65
|
+
for "photos taken with Tri-X that look like they were
|
|
66
|
+
burned onto No. 4 or No. 5 photographic paper".
|
|
67
|
+
Only use floating-point numbers when using this mode;
|
|
68
|
+
numbers around 2.4 will make it look right.
|
|
69
|
+
|
|
70
|
+
POSITIONAL ARGUMENTS
|
|
71
|
+
PATH
|
|
72
|
+
Type: str
|
|
73
|
+
Directory or File Path
|
|
74
|
+
|
|
75
|
+
FLAGS
|
|
76
|
+
--colorautoadjust=COLORAUTOADJUST
|
|
77
|
+
Type: bool
|
|
78
|
+
Default: False
|
|
79
|
+
Use colorautoadjust algorithm. Defaults to False.
|
|
80
|
+
--colorstretch=COLORSTRETCH
|
|
81
|
+
Type: bool
|
|
82
|
+
Default: False
|
|
83
|
+
Use colorstretch algorithm. Defaults to False.
|
|
84
|
+
--grayscale=GRAYSCALE
|
|
85
|
+
Type: bool
|
|
86
|
+
Default: False
|
|
87
|
+
Convert to grayscale. Defaults to False.
|
|
88
|
+
--invert=INVERT
|
|
89
|
+
Type: bool
|
|
90
|
+
Default: False
|
|
91
|
+
Invert color. Defaults to False.
|
|
92
|
+
--tosaka=TOSAKA
|
|
93
|
+
Type: Optional[Optional]
|
|
94
|
+
Default: None
|
|
95
|
+
Use Tosaka mode. Defaults to None.
|
|
96
|
+
--outputrgb=OUTPUTRGB
|
|
97
|
+
Type: bool
|
|
98
|
+
Default: False
|
|
99
|
+
Outputs a monochrome image in RGB. Defaults to False.
|
|
100
|
+
--noise=NOISE
|
|
101
|
+
Type: Optional[Optional]
|
|
102
|
+
Default: None
|
|
103
|
+
Add Gaussian noise. Defaults to None.
|
|
104
|
+
|
|
105
|
+
NOTES
|
|
106
|
+
You can also use flags syntax for POSITIONAL ARGUMENTS
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
If a directory is specified in the path, an `out` directory will be created in the specified directory, and the converted JPEG and PNG images will be stored in PNG format.
|
|
110
|
+
|
|
111
|
+
If you specify a JPEG or PNG image file as the path, an `out` directory will be created in the directory where the image is stored, and the converted image will be stored in PNG format.
|
|
112
|
+
|
|
113
|
+
**Note:** If you specify a file in any other format in the path, be aware there is no error handling. The program will terminate abnormally.
|
|
114
|
+
|
|
115
|
+
## Algorithms
|
|
116
|
+
|
|
117
|
+
The following algorithms are available in this tool.
|
|
118
|
+
|
|
119
|
+
### RGBA to RGB Convert
|
|
120
|
+
|
|
121
|
+
Converts an image that contains Alpha, such as RGBA, to image data that does not contain Alpha.
|
|
122
|
+
Transparent areas will be filled with white.
|
|
123
|
+
|
|
124
|
+
This algorithm is performed on any image file.
|
|
125
|
+
|
|
126
|
+
### invert
|
|
127
|
+
|
|
128
|
+
Inverts the colors of an image using Pillow's built-in algorithm.
|
|
129
|
+
|
|
130
|
+
In the case of negative film, color conversion that takes into account the film base color is not performed, but we plan to follow up with a feature to be developed in the future.
|
|
131
|
+
|
|
132
|
+
### colorautoajust
|
|
133
|
+
|
|
134
|
+
We will use the "automatic color equalization" algorithm described in the following paper to apply color correction.
|
|
135
|
+
|
|
136
|
+
This process is more time consuming than the algorithm used in "colorstretch", but it can reproduce more natural colors.
|
|
137
|
+
|
|
138
|
+
(References)
|
|
139
|
+
|
|
140
|
+
A. Rizzi, C. Gatta and D. Marini, "A new algorithm for unsupervised global and local color correction.", Pattern Recognition Letters, vol. 24, no. 11, 2003.
|
|
141
|
+
|
|
142
|
+
### colorstretch
|
|
143
|
+
|
|
144
|
+
The "gray world" and "stretch" algorithms described in the following paper are combined to apply color correction.
|
|
145
|
+
|
|
146
|
+
This process is faster than the algorithm used in "colorautoajust".
|
|
147
|
+
|
|
148
|
+
(References)
|
|
149
|
+
|
|
150
|
+
D. Nikitenko, M. Wirth and K. Trudel, "Applicability Of White-Balancing Algorithms to Restoring Faded Colour Slides: An Empirical Evaluation.", Journal of Multimedia, vol. 3, no. 5, 2008.
|
|
151
|
+
|
|
152
|
+
### grayscale
|
|
153
|
+
|
|
154
|
+
Convert a color image to grayscale using the algorithm described in the following article.
|
|
155
|
+
|
|
156
|
+
[Python でグレースケール(grayscale)化](https://qiita.com/yoya/items/dba7c40b31f832e9bc2a#pilpillow-%E3%81%A7%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%AB%E5%8C%96-numpy-%E3%81%A7%E4%BD%8E%E8%BC%9D%E5%BA%A6%E5%AF%BE%E5%BF%9C)
|
|
157
|
+
|
|
158
|
+
Note: This article is written in Japanese.
|
|
159
|
+
|
|
160
|
+
### Tosaka mode
|
|
161
|
+
|
|
162
|
+
Tosaka mode is a mode that expresses the preference of Tosaka-senpai, a character in "Kyūkyoku Chōjin R", for "photos taken with Tri-X that look like they were burned onto No. 4 or No. 5 photographic paper".
|
|
163
|
+
|
|
164
|
+
Only use floating-point numbers when using this mode; numbers around 2.4 will make it look right.
|
|
165
|
+
|
|
166
|
+
When this mode is specified, color images will also be converted to grayscale.
|
|
167
|
+
|
|
168
|
+
### outputrgb
|
|
169
|
+
|
|
170
|
+
Outputs a monochrome image in RGB.
|
|
171
|
+
|
|
172
|
+
### noise
|
|
173
|
+
|
|
174
|
+
Add Gaussian noise.
|
|
175
|
+
|
|
176
|
+
To add noise, you need to specify a floating-point number; a value of about 10.0 will be just right.
|
|
177
|
+
|
|
178
|
+
## Special Thanks
|
|
179
|
+
|
|
180
|
+
We are using the following libraries.
|
|
181
|
+
|
|
182
|
+
[shunsukeaihara/colorcorrect](https://github.com/shunsukeaihara/colorcorrect)
|
|
183
|
+
|
anshitsu-0.0.0/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Anshitsu
|
|
2
|
+
|
|
3
|
+
[](https://github.com/huideyeren/anshitsu/actions/workflows/testing.yml)
|
|
4
|
+
|
|
5
|
+
[](https://codecov.io/gh/huideyeren/anshitsu)
|
|
6
|
+
|
|
7
|
+
A tiny digital photographic utility.
|
|
8
|
+
|
|
9
|
+
"Anshitsu" means a darkroom in Japanese.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
Run this command in an environment where Python 3.10 or higher is installed.
|
|
14
|
+
|
|
15
|
+
We have tested it on Windows, Mac, and Ubuntu on GitHub Actions, but we have not tested it on Macs with Apple Silicon, so please use it at your own risk on Macs with Apple Silicon.
|
|
16
|
+
|
|
17
|
+
``` shell
|
|
18
|
+
pip install anshitsu
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
It is as described in the following help.
|
|
24
|
+
|
|
25
|
+
``` shell
|
|
26
|
+
INFO: Showing help with the command 'anshitsu -- --help'.
|
|
27
|
+
|
|
28
|
+
NAME
|
|
29
|
+
anshitsu - Process Runnner for Command Line Interface
|
|
30
|
+
|
|
31
|
+
SYNOPSIS
|
|
32
|
+
anshitsu PATH <flags>
|
|
33
|
+
|
|
34
|
+
DESCRIPTION
|
|
35
|
+
This utility converts the colors of images such as photos.
|
|
36
|
+
|
|
37
|
+
If you specify a directory path, it will convert
|
|
38
|
+
the image files in the specified directory.
|
|
39
|
+
If you specify a file path, it will convert the specified file.
|
|
40
|
+
If you specify an option, the specified conversion will be performed.
|
|
41
|
+
|
|
42
|
+
Tosaka mode is a mode that expresses the preference of
|
|
43
|
+
Tosaka-senpai, a character in "Kyūkyoku Chōjin R",
|
|
44
|
+
for "photos taken with Tri-X that look like they were
|
|
45
|
+
burned onto No. 4 or No. 5 photographic paper".
|
|
46
|
+
Only use floating-point numbers when using this mode;
|
|
47
|
+
numbers around 2.4 will make it look right.
|
|
48
|
+
|
|
49
|
+
POSITIONAL ARGUMENTS
|
|
50
|
+
PATH
|
|
51
|
+
Type: str
|
|
52
|
+
Directory or File Path
|
|
53
|
+
|
|
54
|
+
FLAGS
|
|
55
|
+
--colorautoadjust=COLORAUTOADJUST
|
|
56
|
+
Type: bool
|
|
57
|
+
Default: False
|
|
58
|
+
Use colorautoadjust algorithm. Defaults to False.
|
|
59
|
+
--colorstretch=COLORSTRETCH
|
|
60
|
+
Type: bool
|
|
61
|
+
Default: False
|
|
62
|
+
Use colorstretch algorithm. Defaults to False.
|
|
63
|
+
--grayscale=GRAYSCALE
|
|
64
|
+
Type: bool
|
|
65
|
+
Default: False
|
|
66
|
+
Convert to grayscale. Defaults to False.
|
|
67
|
+
--invert=INVERT
|
|
68
|
+
Type: bool
|
|
69
|
+
Default: False
|
|
70
|
+
Invert color. Defaults to False.
|
|
71
|
+
--tosaka=TOSAKA
|
|
72
|
+
Type: Optional[Optional]
|
|
73
|
+
Default: None
|
|
74
|
+
Use Tosaka mode. Defaults to None.
|
|
75
|
+
--outputrgb=OUTPUTRGB
|
|
76
|
+
Type: bool
|
|
77
|
+
Default: False
|
|
78
|
+
Outputs a monochrome image in RGB. Defaults to False.
|
|
79
|
+
--noise=NOISE
|
|
80
|
+
Type: Optional[Optional]
|
|
81
|
+
Default: None
|
|
82
|
+
Add Gaussian noise. Defaults to None.
|
|
83
|
+
|
|
84
|
+
NOTES
|
|
85
|
+
You can also use flags syntax for POSITIONAL ARGUMENTS
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
If a directory is specified in the path, an `out` directory will be created in the specified directory, and the converted JPEG and PNG images will be stored in PNG format.
|
|
89
|
+
|
|
90
|
+
If you specify a JPEG or PNG image file as the path, an `out` directory will be created in the directory where the image is stored, and the converted image will be stored in PNG format.
|
|
91
|
+
|
|
92
|
+
**Note:** If you specify a file in any other format in the path, be aware there is no error handling. The program will terminate abnormally.
|
|
93
|
+
|
|
94
|
+
## Algorithms
|
|
95
|
+
|
|
96
|
+
The following algorithms are available in this tool.
|
|
97
|
+
|
|
98
|
+
### RGBA to RGB Convert
|
|
99
|
+
|
|
100
|
+
Converts an image that contains Alpha, such as RGBA, to image data that does not contain Alpha.
|
|
101
|
+
Transparent areas will be filled with white.
|
|
102
|
+
|
|
103
|
+
This algorithm is performed on any image file.
|
|
104
|
+
|
|
105
|
+
### invert
|
|
106
|
+
|
|
107
|
+
Inverts the colors of an image using Pillow's built-in algorithm.
|
|
108
|
+
|
|
109
|
+
In the case of negative film, color conversion that takes into account the film base color is not performed, but we plan to follow up with a feature to be developed in the future.
|
|
110
|
+
|
|
111
|
+
### colorautoajust
|
|
112
|
+
|
|
113
|
+
We will use the "automatic color equalization" algorithm described in the following paper to apply color correction.
|
|
114
|
+
|
|
115
|
+
This process is more time consuming than the algorithm used in "colorstretch", but it can reproduce more natural colors.
|
|
116
|
+
|
|
117
|
+
(References)
|
|
118
|
+
|
|
119
|
+
A. Rizzi, C. Gatta and D. Marini, "A new algorithm for unsupervised global and local color correction.", Pattern Recognition Letters, vol. 24, no. 11, 2003.
|
|
120
|
+
|
|
121
|
+
### colorstretch
|
|
122
|
+
|
|
123
|
+
The "gray world" and "stretch" algorithms described in the following paper are combined to apply color correction.
|
|
124
|
+
|
|
125
|
+
This process is faster than the algorithm used in "colorautoajust".
|
|
126
|
+
|
|
127
|
+
(References)
|
|
128
|
+
|
|
129
|
+
D. Nikitenko, M. Wirth and K. Trudel, "Applicability Of White-Balancing Algorithms to Restoring Faded Colour Slides: An Empirical Evaluation.", Journal of Multimedia, vol. 3, no. 5, 2008.
|
|
130
|
+
|
|
131
|
+
### grayscale
|
|
132
|
+
|
|
133
|
+
Convert a color image to grayscale using the algorithm described in the following article.
|
|
134
|
+
|
|
135
|
+
[Python でグレースケール(grayscale)化](https://qiita.com/yoya/items/dba7c40b31f832e9bc2a#pilpillow-%E3%81%A7%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%AB%E5%8C%96-numpy-%E3%81%A7%E4%BD%8E%E8%BC%9D%E5%BA%A6%E5%AF%BE%E5%BF%9C)
|
|
136
|
+
|
|
137
|
+
Note: This article is written in Japanese.
|
|
138
|
+
|
|
139
|
+
### Tosaka mode
|
|
140
|
+
|
|
141
|
+
Tosaka mode is a mode that expresses the preference of Tosaka-senpai, a character in "Kyūkyoku Chōjin R", for "photos taken with Tri-X that look like they were burned onto No. 4 or No. 5 photographic paper".
|
|
142
|
+
|
|
143
|
+
Only use floating-point numbers when using this mode; numbers around 2.4 will make it look right.
|
|
144
|
+
|
|
145
|
+
When this mode is specified, color images will also be converted to grayscale.
|
|
146
|
+
|
|
147
|
+
### outputrgb
|
|
148
|
+
|
|
149
|
+
Outputs a monochrome image in RGB.
|
|
150
|
+
|
|
151
|
+
### noise
|
|
152
|
+
|
|
153
|
+
Add Gaussian noise.
|
|
154
|
+
|
|
155
|
+
To add noise, you need to specify a floating-point number; a value of about 10.0 will be just right.
|
|
156
|
+
|
|
157
|
+
## Special Thanks
|
|
158
|
+
|
|
159
|
+
We are using the following libraries.
|
|
160
|
+
|
|
161
|
+
[shunsukeaihara/colorcorrect](https://github.com/shunsukeaihara/colorcorrect)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "anshitsu"
|
|
3
|
+
version = "0.0.0" # using poetry-dynamic-versioning
|
|
4
|
+
description = "A tiny digital photographic utility."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = ["Iosif Takakura <iosif@huideyeren.info>"]
|
|
7
|
+
homepage = "https://github.com/huideyeren"
|
|
8
|
+
repository = "https://github.com/huideyeren/anshitsu"
|
|
9
|
+
documentation = "https://github.com/huideyeren/anshitsu"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
|
|
12
|
+
[tool.poetry-dynamic-versioning]
|
|
13
|
+
enable = true
|
|
14
|
+
style = "pep440"
|
|
15
|
+
|
|
16
|
+
[tool.poetry.dependencies]
|
|
17
|
+
python = "<3.13,>=3.10"
|
|
18
|
+
numpy = "^1.26.0"
|
|
19
|
+
Pillow = "^10.1.0"
|
|
20
|
+
colorcorrect = "^0.9.1"
|
|
21
|
+
fire = "^0.5.0"
|
|
22
|
+
|
|
23
|
+
[tool.poetry.dev-dependencies]
|
|
24
|
+
pytest = "^6.2"
|
|
25
|
+
flake8 = "^6.1.0"
|
|
26
|
+
mypy = "^0.981"
|
|
27
|
+
black = "^23.10"
|
|
28
|
+
isort = "^5.12.0"
|
|
29
|
+
pyproject-flake8 = "^0.0.1a4"
|
|
30
|
+
pytest-randomly = "^3.15.0"
|
|
31
|
+
tox = "^4.11.3"
|
|
32
|
+
pytest-cov = "^4.1.0"
|
|
33
|
+
|
|
34
|
+
[tool.poetry.scripts]
|
|
35
|
+
anshitsu = "anshitsu.cli:main"
|
|
36
|
+
|
|
37
|
+
[tool.poetry.group.dev.dependencies]
|
|
38
|
+
importlib-metadata = "^6.8.0"
|
|
39
|
+
poetry-dynamic-versioning = "^1.1.1"
|
|
40
|
+
setuptools = "^68.2.2"
|
|
41
|
+
|
|
42
|
+
[build-system]
|
|
43
|
+
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
|
|
44
|
+
build-backend = "poetry_dynamic_versioning.backend"
|
|
45
|
+
|
|
46
|
+
[tool.black]
|
|
47
|
+
line-length = 88
|
|
48
|
+
|
|
49
|
+
[tool.mypy]
|
|
50
|
+
ignore_missing_imports=1
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version: str = "v1.5.0"
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import glob
|
|
3
|
+
import os
|
|
4
|
+
import os.path
|
|
5
|
+
import re
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import fire
|
|
9
|
+
import fire.core
|
|
10
|
+
from PIL import Image, UnidentifiedImageError
|
|
11
|
+
|
|
12
|
+
from anshitsu.retouch import Retouch
|
|
13
|
+
from anshitsu.__version__ import version as __version__
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def process(
|
|
17
|
+
path: Optional[str] = None,
|
|
18
|
+
colorautoadjust: bool = False,
|
|
19
|
+
colorstretch: bool = False,
|
|
20
|
+
grayscale: bool = False,
|
|
21
|
+
invert: bool = False,
|
|
22
|
+
tosaka: Optional[float] = None,
|
|
23
|
+
outputrgb: bool = False,
|
|
24
|
+
noise: Optional[float] = None,
|
|
25
|
+
overwrite: bool = False,
|
|
26
|
+
version: bool = False,
|
|
27
|
+
) -> str:
|
|
28
|
+
"""
|
|
29
|
+
Process Runnner for Command Line Interface
|
|
30
|
+
|
|
31
|
+
This utility converts the colors of images such as photos.
|
|
32
|
+
|
|
33
|
+
If you specify a directory path, it will convert
|
|
34
|
+
the image files in the specified directory.
|
|
35
|
+
If you specify a file path, it will convert the specified file.
|
|
36
|
+
If you specify an option, the specified conversion will be performed.
|
|
37
|
+
|
|
38
|
+
Tosaka mode is a mode that expresses the preference of
|
|
39
|
+
Tosaka-senpai, a character in "Kyūkyoku Chōjin R",
|
|
40
|
+
for "photos taken with Tri-X that look like they were
|
|
41
|
+
burned onto No. 4 or No. 5 photographic paper".
|
|
42
|
+
Only use floating-point numbers when using this mode;
|
|
43
|
+
numbers around 2.4 will make it look right.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
path (Optional[str], optional): Directory or File Path. Defaults to None.
|
|
47
|
+
overwrite (bool, optional): Overwrite original files. Defaults to False.
|
|
48
|
+
colorautoadjust (bool, optional): Use colorautoadjust algorithm. Defaults to False.
|
|
49
|
+
colorstretch (bool, optional): Use colorstretch algorithm. Defaults to False.
|
|
50
|
+
grayscale (bool, optional): Convert to grayscale. Defaults to False.
|
|
51
|
+
invert (bool, optional): Invert color. Defaults to False.
|
|
52
|
+
tosaka (Optional[float], optional): Use Tosaka mode. Defaults to None.
|
|
53
|
+
outputrgb (bool, optional): Outputs a monochrome image in RGB. Defaults to False.
|
|
54
|
+
noise (Optional[float], optional): Add Gaussian noise. Defaults to None.
|
|
55
|
+
version (bool, optional): Show version. Defaults to False.
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
fire.core.FireError: Error that occurs when the specified string is not a path.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
str: Message.
|
|
62
|
+
"""
|
|
63
|
+
if version:
|
|
64
|
+
return "Anshitsu version {}".format(__version__)
|
|
65
|
+
if path is None:
|
|
66
|
+
raise fire.core.FireError("No path specified!")
|
|
67
|
+
types = ("*.jpg", "*.JPG", "*.jpeg", "*.JPEG", "*.png", "*.PNG")
|
|
68
|
+
files_glob = []
|
|
69
|
+
return_path = ""
|
|
70
|
+
now_s = datetime.datetime.now()
|
|
71
|
+
output_dir = "anshitsu_out"
|
|
72
|
+
original_dir = "anshitsu_orig"
|
|
73
|
+
if os.path.isdir(path):
|
|
74
|
+
for type in types:
|
|
75
|
+
files_glob.extend(glob.glob(os.path.join(path, '**', type), recursive=True))
|
|
76
|
+
files_glob = [file for file in files_glob if not file.__contains__(output_dir)]
|
|
77
|
+
return_path = path
|
|
78
|
+
|
|
79
|
+
if len(files_glob) == 0:
|
|
80
|
+
raise fire.core.FireError(
|
|
81
|
+
"There are no JPEG or PNG files in this directory."
|
|
82
|
+
)
|
|
83
|
+
elif os.path.isfile(path):
|
|
84
|
+
files_glob.extend(glob.glob(path))
|
|
85
|
+
return_path = os.path.abspath(os.path.join(path, os.pardir))
|
|
86
|
+
else:
|
|
87
|
+
raise fire.core.FireError("A non-path string was passed.")
|
|
88
|
+
if overwrite is True:
|
|
89
|
+
os.makedirs(os.path.join(return_path, original_dir))
|
|
90
|
+
for i, file in enumerate(files_glob):
|
|
91
|
+
try:
|
|
92
|
+
image = Image.open(file)
|
|
93
|
+
except UnidentifiedImageError as e:
|
|
94
|
+
raise fire.core.FireError(e)
|
|
95
|
+
exif = image.getexif()
|
|
96
|
+
original_filename: str = os.path.split(file)[1]
|
|
97
|
+
extension = original_filename.split(".")[-1]
|
|
98
|
+
timestamp = now_s.strftime("%Y-%m-%d_%H-%M-%S")
|
|
99
|
+
if overwrite is True:
|
|
100
|
+
backup_filename = original_filename
|
|
101
|
+
image.save(os.path.join(
|
|
102
|
+
return_path,
|
|
103
|
+
original_dir,
|
|
104
|
+
backup_filename
|
|
105
|
+
))
|
|
106
|
+
filename = os.path.join(return_path, re.sub(r"\.[^.]+$", "", original_filename) + ".png")
|
|
107
|
+
remove_file_list = [".jpg", ".JPG", ".jpeg", ".JPEG", ".PNG"]
|
|
108
|
+
for remove_file in remove_file_list:
|
|
109
|
+
remove_file_name = re.sub(r"\.[^.]+$", "", original_filename) + remove_file
|
|
110
|
+
remove_file_path = os.path.join(return_path, remove_file_name)
|
|
111
|
+
if os.path.isfile(remove_file_path):
|
|
112
|
+
os.remove(remove_file_path)
|
|
113
|
+
else:
|
|
114
|
+
filename = os.path.join(
|
|
115
|
+
return_path,
|
|
116
|
+
output_dir,
|
|
117
|
+
re.sub(r"\.[^.]+$", "_", original_filename)
|
|
118
|
+
+ "_{0}_converted_at_{1}.png".format(extension, timestamp))
|
|
119
|
+
retouch = Retouch(
|
|
120
|
+
image=image,
|
|
121
|
+
colorautoadjust=colorautoadjust,
|
|
122
|
+
colorstretch=colorstretch,
|
|
123
|
+
grayscale=grayscale,
|
|
124
|
+
invert=invert,
|
|
125
|
+
tosaka=tosaka,
|
|
126
|
+
outputrgb=outputrgb,
|
|
127
|
+
noise=noise,
|
|
128
|
+
)
|
|
129
|
+
saved_image = retouch.process()
|
|
130
|
+
os.makedirs(os.path.join(return_path, output_dir), exist_ok=True)
|
|
131
|
+
saved_image.save(
|
|
132
|
+
filename,
|
|
133
|
+
quality=100, # Specify 100 as the highest image quality
|
|
134
|
+
subsampling=0,
|
|
135
|
+
exif=exif,
|
|
136
|
+
)
|
|
137
|
+
print("{0}/{1} done!".format((i + 1), str(len(files_glob))))
|
|
138
|
+
|
|
139
|
+
return "The process was completed successfully."
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
from collections import namedtuple
|
|
2
|
+
from typing import Optional, Tuple
|
|
3
|
+
|
|
4
|
+
import colorcorrect.algorithm as cca
|
|
5
|
+
import numpy as np
|
|
6
|
+
from colorcorrect.util import from_pil, to_pil
|
|
7
|
+
from PIL import Image, ImageChops, ImageEnhance, ImageOps
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Retouch:
|
|
11
|
+
"""
|
|
12
|
+
Perform retouching.
|
|
13
|
+
|
|
14
|
+
Passing an image and options to the constructor will convert the specified image.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
RGB_RED_VALUE: int = 255
|
|
18
|
+
RGB_GREEN_VALUE: int = 255
|
|
19
|
+
RGB_BLUE_VALUE: int = 255
|
|
20
|
+
RGB_WHITE_COLOR: Tuple[int, int, int] = (
|
|
21
|
+
RGB_RED_VALUE,
|
|
22
|
+
RGB_GREEN_VALUE,
|
|
23
|
+
RGB_BLUE_VALUE,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
image: Image,
|
|
29
|
+
colorautoadjust: bool = False,
|
|
30
|
+
colorstretch: bool = False,
|
|
31
|
+
grayscale: bool = False,
|
|
32
|
+
invert: bool = False,
|
|
33
|
+
tosaka: Optional[float] = None,
|
|
34
|
+
outputrgb: bool = False,
|
|
35
|
+
noise: Optional[float] = None,
|
|
36
|
+
) -> None:
|
|
37
|
+
"""
|
|
38
|
+
__init__ constructor.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
image (Image): Image file.
|
|
42
|
+
colorautoadjust (bool, optional): Use colorautoadjust algorithm. Defaults to False.
|
|
43
|
+
colorstretch (bool, optional): Use colorstretch algorithm. Defaults to False.
|
|
44
|
+
grayscale (bool, optional): Convert to grayscale. Defaults to False.
|
|
45
|
+
invert (bool, optional): Invert color. Defaults to False.
|
|
46
|
+
tosaka (Optional[float], optional): Use Tosaka mode. Defaults to None.
|
|
47
|
+
outputrgb (bool, optional): Outputs a monochrome image in RGB. Defaults to False.
|
|
48
|
+
noise (Optional[float], optional): Add Gaussian noise. Defaults to None.
|
|
49
|
+
"""
|
|
50
|
+
self.image = image
|
|
51
|
+
self.colorautoadjust = colorautoadjust
|
|
52
|
+
self.colorstretch = colorstretch
|
|
53
|
+
self.grayscale = grayscale
|
|
54
|
+
self.invert = invert
|
|
55
|
+
self.tosaka = tosaka
|
|
56
|
+
self.output_rgb = outputrgb
|
|
57
|
+
self.noise = noise
|
|
58
|
+
|
|
59
|
+
def process(self) -> Image:
|
|
60
|
+
self.image = self.__rgba_convert()
|
|
61
|
+
|
|
62
|
+
if self.invert:
|
|
63
|
+
self.image = self.__invert()
|
|
64
|
+
|
|
65
|
+
if self.colorautoadjust:
|
|
66
|
+
self.image = self.__colorautoadjust()
|
|
67
|
+
|
|
68
|
+
if self.colorstretch:
|
|
69
|
+
self.image = self.__colorstretch()
|
|
70
|
+
|
|
71
|
+
if self.grayscale:
|
|
72
|
+
self.image = self.__grayscale()
|
|
73
|
+
|
|
74
|
+
if self.noise is not None:
|
|
75
|
+
self.image = self.__noise()
|
|
76
|
+
|
|
77
|
+
if self.tosaka is not None:
|
|
78
|
+
self.image = self.__tosaka()
|
|
79
|
+
|
|
80
|
+
if self.output_rgb:
|
|
81
|
+
self.image = self.__output_rgb()
|
|
82
|
+
|
|
83
|
+
return self.image
|
|
84
|
+
|
|
85
|
+
def __colorautoadjust(self) -> Image:
|
|
86
|
+
"""
|
|
87
|
+
__colorautoadjust
|
|
88
|
+
|
|
89
|
+
Use colorautoadjust algorithm.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Image: processed image.
|
|
93
|
+
"""
|
|
94
|
+
if self.image.mode == "L":
|
|
95
|
+
return self.image
|
|
96
|
+
return to_pil(cca.automatic_color_equalization(from_pil(self.image)))
|
|
97
|
+
|
|
98
|
+
def __colorstretch(self) -> Image:
|
|
99
|
+
"""
|
|
100
|
+
__colorstretch
|
|
101
|
+
|
|
102
|
+
Use colorstretch algorithm.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Image: processed image.
|
|
106
|
+
"""
|
|
107
|
+
if self.image.mode == "L":
|
|
108
|
+
return self.image
|
|
109
|
+
return to_pil(cca.stretch(cca.grey_world(from_pil(self.image))))
|
|
110
|
+
|
|
111
|
+
def __noise(self) -> Image:
|
|
112
|
+
"""
|
|
113
|
+
__noise
|
|
114
|
+
|
|
115
|
+
Add Gaussian noise.
|
|
116
|
+
To add noise, you need to specify floating-point numbers;
|
|
117
|
+
a value of about 10.0 will be just right.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Image: processed image.
|
|
121
|
+
"""
|
|
122
|
+
table = [x * 2 for x in range(256)] * len(self.image.getbands())
|
|
123
|
+
if self.image.mode == "RGB":
|
|
124
|
+
noise_image = Image.effect_noise(
|
|
125
|
+
(self.image.width, self.image.height), self.noise
|
|
126
|
+
).convert("RGB")
|
|
127
|
+
self.image = ImageChops.multiply(self.image, noise_image).point(table)
|
|
128
|
+
if self.image.mode == "L":
|
|
129
|
+
noise_image = Image.effect_noise(
|
|
130
|
+
(self.image.width, self.image.height), self.noise
|
|
131
|
+
)
|
|
132
|
+
self.image = ImageChops.multiply(self.image, noise_image).point(table)
|
|
133
|
+
return self.image
|
|
134
|
+
|
|
135
|
+
def __grayscale(self) -> Image:
|
|
136
|
+
"""
|
|
137
|
+
__grayscale
|
|
138
|
+
|
|
139
|
+
Converts to grayscale based on the luminance in the CIE XYZ color space.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Image: processed image.
|
|
143
|
+
"""
|
|
144
|
+
if self.image.mode == "L":
|
|
145
|
+
return self.image
|
|
146
|
+
|
|
147
|
+
rgb = np.array(self.image, dtype="float32")
|
|
148
|
+
|
|
149
|
+
GAMMA = 2.2
|
|
150
|
+
ColorMatrix = namedtuple("ColorMatrix", ("red", "green", "blue"))
|
|
151
|
+
|
|
152
|
+
# Coefficients for luminance in CIE XYZ color space
|
|
153
|
+
CIE_XYZ = ColorMatrix(0.2126, 0.7152, 0.0722)
|
|
154
|
+
|
|
155
|
+
# Inverse Gamma Correction
|
|
156
|
+
rgbL = pow(rgb / 255.0, GAMMA)
|
|
157
|
+
|
|
158
|
+
# Extract RGB values
|
|
159
|
+
r, g, b = rgbL[:, :, 0], rgbL[:, :, 1], rgbL[:, :, 2]
|
|
160
|
+
|
|
161
|
+
# Convert to grayscale
|
|
162
|
+
grayL = CIE_XYZ.red * r + CIE_XYZ.green * g + CIE_XYZ.blue * b
|
|
163
|
+
|
|
164
|
+
# gamma correction
|
|
165
|
+
gray = pow(grayL, 1.0 / GAMMA) * 255
|
|
166
|
+
|
|
167
|
+
self.image = Image.fromarray(gray.astype("uint8"))
|
|
168
|
+
|
|
169
|
+
return self.image
|
|
170
|
+
|
|
171
|
+
def __invert(self) -> Image:
|
|
172
|
+
"""
|
|
173
|
+
__invert
|
|
174
|
+
|
|
175
|
+
Invert color.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
Image: processed image.
|
|
179
|
+
"""
|
|
180
|
+
return ImageOps.invert(self.image)
|
|
181
|
+
|
|
182
|
+
def __tosaka(self) -> Image:
|
|
183
|
+
"""
|
|
184
|
+
__tosaka
|
|
185
|
+
|
|
186
|
+
Use Tosaka mode.
|
|
187
|
+
|
|
188
|
+
Tosaka mode is a mode that expresses the preference of
|
|
189
|
+
Tosaka-senpai, a character in "Kyūkyoku Chōjin R",
|
|
190
|
+
for "photos taken with Tri-X that look like they were
|
|
191
|
+
burned onto No. 4 or No. 5 photographic paper".
|
|
192
|
+
Only use floating-point numbers when using this mode;
|
|
193
|
+
numbers around 2.4 will make it look right.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Image: processed image.
|
|
197
|
+
"""
|
|
198
|
+
if self.image.mode != "L":
|
|
199
|
+
self.image = self.__grayscale()
|
|
200
|
+
imageC = ImageEnhance.Contrast(self.image)
|
|
201
|
+
self.image = imageC.enhance(self.tosaka)
|
|
202
|
+
return self.image
|
|
203
|
+
|
|
204
|
+
def __rgba_convert(self) -> Image:
|
|
205
|
+
"""
|
|
206
|
+
__rgba_convert
|
|
207
|
+
|
|
208
|
+
Converts image data that contains transparency to image data that does not contain transparency.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Image: processed image.
|
|
212
|
+
"""
|
|
213
|
+
if self.image.mode == "RGBA":
|
|
214
|
+
self.image.load()
|
|
215
|
+
background = Image.new("RGB", self.image.size, self.RGB_WHITE_COLOR)
|
|
216
|
+
background.paste(self.image, mask=self.image.split()[3])
|
|
217
|
+
self.image = background
|
|
218
|
+
if self.image.mode == "LA":
|
|
219
|
+
self.image.load()
|
|
220
|
+
background = Image.new("L", self.image.size, 255)
|
|
221
|
+
background.paste(self.image, mask=self.image.split()[1])
|
|
222
|
+
self.image = background
|
|
223
|
+
return self.image
|
|
224
|
+
|
|
225
|
+
def __output_rgb(self) -> Image:
|
|
226
|
+
"""
|
|
227
|
+
__output_rgb
|
|
228
|
+
|
|
229
|
+
Outputs a monochrome image in RGB.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Image: processed image.
|
|
233
|
+
"""
|
|
234
|
+
if self.image.mode == "L":
|
|
235
|
+
self.image = self.image.convert("RGB")
|
|
236
|
+
return self.image
|