From 62997549644fc193938aaf851597c2b19c3d9c4f Mon Sep 17 00:00:00 2001 From: Alex Xu Date: Sun, 13 Jul 2025 21:52:17 -0700 Subject: [PATCH] draft: add black point level (#1028) * initial black point * convert to L * add GUI --- README.md | 1 + gui/KCC.ui | 272 ++++++++++++++-------------- kindlecomicconverter/KCC_gui.py | 3 + kindlecomicconverter/KCC_ui.py | 243 +++++++++++++------------ kindlecomicconverter/comic2ebook.py | 2 + kindlecomicconverter/image.py | 12 ++ 6 files changed, 285 insertions(+), 248 deletions(-) diff --git a/README.md b/README.md index 5cbf6be..b627d92 100644 --- a/README.md +++ b/README.md @@ -224,6 +224,7 @@ PROCESSING: Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0] -g GAMMA, --gamma GAMMA Apply gamma correction to linearize the image [Default=Auto] + --autolevel Set most common dark pixel value to be black point for leveling. -c CROPPING, --cropping CROPPING Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2] --cp CROPPINGP, --croppingpower CROPPINGP diff --git a/gui/KCC.ui b/gui/KCC.ui index b1aafaa..8b40677 100644 --- a/gui/KCC.ui +++ b/gui/KCC.ui @@ -453,6 +453,39 @@ 0 + + + + <html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html> + + + Custom gamma + + + + + + + <html><head/><body><p style='white-space:pre'>Enable right-to-left reading.</p></body></html> + + + Right-to-left mode + + + + + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html> + + + W/B margins + + + true + + + @@ -466,13 +499,13 @@ - - + + - <html><head/><body><p style='white-space:pre'>Enable right-to-left reading.</p></body></html> + <html><head/><body><p>Combines all selected files into a single file. (Helpful for combining chapters into volumes.)</p></body></html> - Right-to-left mode + File Fusion @@ -498,46 +531,56 @@ - - + + - <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Disabled</span></p><p>Disabled</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Margins<br/></span>Margins</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Margins + page numbers<br/></span>Margins +page numbers</p></body></html> + <html><head/><body><p>When the spread splitter option is partially checked,</p><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Rotate Last<br/></span>Put the rotated 2 page spread after the split spreads.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Rotate First<br/></span>Put the rotated 2 page spread before the split spreads.</p></body></html> - Cropping mode + Rotate First + + + + + + + Reduce rainbow effect on color eink by slightly blurring images + + + Rainbow blur + + + + + + + <html><head/><body><p><span style=" font-weight:700; text-decoration: underline;">Unchecked<br/></span>Maximal output file size is 100 MB for Webtoon, 400 MB for others before split occurs.</p><p><span style=" font-weight:700; text-decoration: underline;">Checked</span><br/>Output file size specified in &quot;Chunk size MB&quot; before split occurs.</p></body></html> + + + Chunk size + + + + + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Split and rotate<br/></span>Double page spreads will be displayed twice. First split and then rotated. </p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html> + + + Spread splitter true - - + + - <html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html> + <html><head/><body><p style='white-space:pre'><span style=" font-weight:600; text-decoration: underline;">Unchecked - Automatic mode<br/></span>The output will be split automatically.</p><p style='white-space:pre'><span style=" font-weight:600; text-decoration: underline;">Checked - Volume mode<br/></span>Every subdirectory will be considered as a separate volume.</p></body></html> - Webtoon mode - - - - - - - <html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html> - - - Color mode - - - - - - - Delete input file(s) or directory. It's not recoverable! - - - Delete input + Output split @@ -564,56 +607,13 @@ - - + + - Reduce rainbow effect on color eink by slightly blurring images + Shift first page to opposite side in landscape for two page spread alignment - Rainbow blur - - - - - - - Do not rotate double page spreads in spread splitter option. - - - No rotate - - - - - - - <html><head/><body><p>Combines all selected files into a single file. (Helpful for combining chapters into volumes.)</p></body></html> - - - File Fusion - - - - - - - <html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html> - - - Custom gamma - - - - - - - <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - JPEG<br/></span>Use JPEG files</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - force PNG<br/></span>Create PNG files instead JPEG</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - mozJpeg<br/></span>10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2</p></body></html> - - - JPEG/PNG/mozJpeg - - - true + Spread shift @@ -627,69 +627,33 @@ - - + + - <html><head/><body><p><span style=" font-weight:700; text-decoration: underline;">Unchecked<br/></span>Maximal output file size is 100 MB for Webtoon, 400 MB for others before split occurs.</p><p><span style=" font-weight:700; text-decoration: underline;">Checked</span><br/>Output file size specified in &quot;Chunk size MB&quot; before split occurs.</p></body></html> + <html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html> - Chunk size + Webtoon mode - - + + - Shift first page to opposite side in landscape for two page spread alignment + <html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html> - Spread shift + Color mode - - + + - <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html> + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Disabled</span></p><p>Disabled</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Margins<br/></span>Margins</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Margins + page numbers<br/></span>Margins +page numbers</p></body></html> - Stretch/Upscale - - - true - - - - - - - <html><head/><body><p style='white-space:pre'><span style=" font-weight:600; text-decoration: underline;">Unchecked - Automatic mode<br/></span>The output will be split automatically.</p><p style='white-space:pre'><span style=" font-weight:600; text-decoration: underline;">Checked - Volume mode<br/></span>Every subdirectory will be considered as a separate volume.</p></body></html> - - - Output split - - - - - - - <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Split and rotate<br/></span>Double page spreads will be displayed twice. First split and then rotated. </p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html> - - - Spread splitter - - - true - - - - - - - <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html> - - - W/B margins + Cropping mode true @@ -706,13 +670,59 @@ - - + + - <html><head/><body><p>When the spread splitter option is partially checked,</p><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Rotate Last<br/></span>Put the rotated 2 page spread after the split spreads.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Rotate First<br/></span>Put the rotated 2 page spread before the split spreads.</p></body></html> + Do not rotate double page spreads in spread splitter option. - Rotate First + No rotate + + + + + + + Delete input file(s) or directory. It's not recoverable! + + + Delete input + + + + + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html> + + + Stretch/Upscale + + + true + + + + + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - JPEG<br/></span>Use JPEG files</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - force PNG<br/></span>Create PNG files instead JPEG</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - mozJpeg<br/></span>10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2</p></body></html> + + + JPEG/PNG/mozJpeg + + + true + + + + + + + <html><head/><body><p>Set the most common dark pixel value to be the black point for leveling on a page by page basis.</p><p>Skipped for any images that were originally color.</p><p>Useful for very gray faded content. Reccomended to use with Custom Gamma = 1.0 (Disabled).</p></body></html> + + + Aggressive Black Point diff --git a/kindlecomicconverter/KCC_gui.py b/kindlecomicconverter/KCC_gui.py index 67c6d20..4744c08 100644 --- a/kindlecomicconverter/KCC_gui.py +++ b/kindlecomicconverter/KCC_gui.py @@ -247,6 +247,8 @@ class WorkerThread(QThread): options.upscale = True if GUI.gammaBox.isChecked() and float(GUI.gammaValue) > 0.09: options.gamma = float(GUI.gammaValue) + if GUI.autoLevelBox.isChecked(): + options.autolevel = True options.cropping = GUI.croppingBox.checkState().value if GUI.croppingBox.checkState() != Qt.CheckState.Unchecked: options.croppingp = float(GUI.croppingPowerValue) @@ -863,6 +865,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow): 'rotateBox': GUI.rotateBox.checkState().value, 'qualityBox': GUI.qualityBox.checkState().value, 'gammaBox': GUI.gammaBox.checkState().value, + 'autoLevelBox': GUI.autoLevelBox.checkState().value, 'croppingBox': GUI.croppingBox.checkState().value, 'croppingPowerSlider': float(self.croppingPowerValue) * 100, 'preserveMarginBox': self.preserveMarginBox.value(), diff --git a/kindlecomicconverter/KCC_ui.py b/kindlecomicconverter/KCC_ui.py index 69720d9..b9d08fb 100644 --- a/kindlecomicconverter/KCC_ui.py +++ b/kindlecomicconverter/KCC_ui.py @@ -251,16 +251,32 @@ class Ui_mainWindow(object): self.gridLayout_2 = QGridLayout(self.optionWidget) self.gridLayout_2.setObjectName(u"gridLayout_2") self.gridLayout_2.setContentsMargins(0, 0, 0, 0) + self.gammaBox = QCheckBox(self.optionWidget) + self.gammaBox.setObjectName(u"gammaBox") + + self.gridLayout_2.addWidget(self.gammaBox, 2, 2, 1, 1) + + self.mangaBox = QCheckBox(self.optionWidget) + self.mangaBox.setObjectName(u"mangaBox") + + self.gridLayout_2.addWidget(self.mangaBox, 1, 0, 1, 1) + + self.borderBox = QCheckBox(self.optionWidget) + self.borderBox.setObjectName(u"borderBox") + self.borderBox.setTristate(True) + + self.gridLayout_2.addWidget(self.borderBox, 3, 0, 1, 1) + self.interPanelCropBox = QCheckBox(self.optionWidget) self.interPanelCropBox.setObjectName(u"interPanelCropBox") self.interPanelCropBox.setTristate(True) self.gridLayout_2.addWidget(self.interPanelCropBox, 6, 2, 1, 1) - self.mangaBox = QCheckBox(self.optionWidget) - self.mangaBox.setObjectName(u"mangaBox") + self.fileFusionBox = QCheckBox(self.optionWidget) + self.fileFusionBox.setObjectName(u"fileFusionBox") - self.gridLayout_2.addWidget(self.mangaBox, 1, 0, 1, 1) + self.gridLayout_2.addWidget(self.fileFusionBox, 6, 0, 1, 1) self.authorEdit = QLineEdit(self.optionWidget) self.authorEdit.setObjectName(u"authorEdit") @@ -274,26 +290,31 @@ class Ui_mainWindow(object): self.gridLayout_2.addWidget(self.authorEdit, 0, 0, 1, 1) - self.croppingBox = QCheckBox(self.optionWidget) - self.croppingBox.setObjectName(u"croppingBox") - self.croppingBox.setTristate(True) + self.rotateFirstBox = QCheckBox(self.optionWidget) + self.rotateFirstBox.setObjectName(u"rotateFirstBox") - self.gridLayout_2.addWidget(self.croppingBox, 4, 2, 1, 1) + self.gridLayout_2.addWidget(self.rotateFirstBox, 8, 1, 1, 1) - self.webtoonBox = QCheckBox(self.optionWidget) - self.webtoonBox.setObjectName(u"webtoonBox") + self.reduceRainbowBox = QCheckBox(self.optionWidget) + self.reduceRainbowBox.setObjectName(u"reduceRainbowBox") - self.gridLayout_2.addWidget(self.webtoonBox, 2, 0, 1, 1) + self.gridLayout_2.addWidget(self.reduceRainbowBox, 7, 2, 1, 1) - self.colorBox = QCheckBox(self.optionWidget) - self.colorBox.setObjectName(u"colorBox") + self.chunkSizeCheckBox = QCheckBox(self.optionWidget) + self.chunkSizeCheckBox.setObjectName(u"chunkSizeCheckBox") - self.gridLayout_2.addWidget(self.colorBox, 3, 2, 1, 1) + self.gridLayout_2.addWidget(self.chunkSizeCheckBox, 7, 1, 1, 1) - self.deleteBox = QCheckBox(self.optionWidget) - self.deleteBox.setObjectName(u"deleteBox") + self.rotateBox = QCheckBox(self.optionWidget) + self.rotateBox.setObjectName(u"rotateBox") + self.rotateBox.setTristate(True) - self.gridLayout_2.addWidget(self.deleteBox, 5, 1, 1, 1) + self.gridLayout_2.addWidget(self.rotateBox, 1, 1, 1, 1) + + self.outputSplit = QCheckBox(self.optionWidget) + self.outputSplit.setObjectName(u"outputSplit") + + self.gridLayout_2.addWidget(self.outputSplit, 3, 1, 1, 1) self.comicinfoTitleBox = QCheckBox(self.optionWidget) self.comicinfoTitleBox.setObjectName(u"comicinfoTitleBox") @@ -306,46 +327,46 @@ class Ui_mainWindow(object): self.gridLayout_2.addWidget(self.qualityBox, 1, 2, 1, 1) - self.reduceRainbowBox = QCheckBox(self.optionWidget) - self.reduceRainbowBox.setObjectName(u"reduceRainbowBox") + self.spreadShiftBox = QCheckBox(self.optionWidget) + self.spreadShiftBox.setObjectName(u"spreadShiftBox") - self.gridLayout_2.addWidget(self.reduceRainbowBox, 7, 2, 1, 1) - - self.noRotateBox = QCheckBox(self.optionWidget) - self.noRotateBox.setObjectName(u"noRotateBox") - - self.gridLayout_2.addWidget(self.noRotateBox, 6, 1, 1, 1) - - self.fileFusionBox = QCheckBox(self.optionWidget) - self.fileFusionBox.setObjectName(u"fileFusionBox") - - self.gridLayout_2.addWidget(self.fileFusionBox, 6, 0, 1, 1) - - self.gammaBox = QCheckBox(self.optionWidget) - self.gammaBox.setObjectName(u"gammaBox") - - self.gridLayout_2.addWidget(self.gammaBox, 2, 2, 1, 1) - - self.mozJpegBox = QCheckBox(self.optionWidget) - self.mozJpegBox.setObjectName(u"mozJpegBox") - self.mozJpegBox.setTristate(True) - - self.gridLayout_2.addWidget(self.mozJpegBox, 4, 0, 1, 1) + self.gridLayout_2.addWidget(self.spreadShiftBox, 5, 0, 1, 1) self.disableProcessingBox = QCheckBox(self.optionWidget) self.disableProcessingBox.setObjectName(u"disableProcessingBox") self.gridLayout_2.addWidget(self.disableProcessingBox, 5, 2, 1, 1) - self.chunkSizeCheckBox = QCheckBox(self.optionWidget) - self.chunkSizeCheckBox.setObjectName(u"chunkSizeCheckBox") + self.webtoonBox = QCheckBox(self.optionWidget) + self.webtoonBox.setObjectName(u"webtoonBox") - self.gridLayout_2.addWidget(self.chunkSizeCheckBox, 7, 1, 1, 1) + self.gridLayout_2.addWidget(self.webtoonBox, 2, 0, 1, 1) - self.spreadShiftBox = QCheckBox(self.optionWidget) - self.spreadShiftBox.setObjectName(u"spreadShiftBox") + self.colorBox = QCheckBox(self.optionWidget) + self.colorBox.setObjectName(u"colorBox") - self.gridLayout_2.addWidget(self.spreadShiftBox, 5, 0, 1, 1) + self.gridLayout_2.addWidget(self.colorBox, 3, 2, 1, 1) + + self.croppingBox = QCheckBox(self.optionWidget) + self.croppingBox.setObjectName(u"croppingBox") + self.croppingBox.setTristate(True) + + self.gridLayout_2.addWidget(self.croppingBox, 4, 2, 1, 1) + + self.maximizeStrips = QCheckBox(self.optionWidget) + self.maximizeStrips.setObjectName(u"maximizeStrips") + + self.gridLayout_2.addWidget(self.maximizeStrips, 4, 1, 1, 1) + + self.noRotateBox = QCheckBox(self.optionWidget) + self.noRotateBox.setObjectName(u"noRotateBox") + + self.gridLayout_2.addWidget(self.noRotateBox, 6, 1, 1, 1) + + self.deleteBox = QCheckBox(self.optionWidget) + self.deleteBox.setObjectName(u"deleteBox") + + self.gridLayout_2.addWidget(self.deleteBox, 5, 1, 1, 1) self.upscaleBox = QCheckBox(self.optionWidget) self.upscaleBox.setObjectName(u"upscaleBox") @@ -353,32 +374,16 @@ class Ui_mainWindow(object): self.gridLayout_2.addWidget(self.upscaleBox, 2, 1, 1, 1) - self.outputSplit = QCheckBox(self.optionWidget) - self.outputSplit.setObjectName(u"outputSplit") + self.mozJpegBox = QCheckBox(self.optionWidget) + self.mozJpegBox.setObjectName(u"mozJpegBox") + self.mozJpegBox.setTristate(True) - self.gridLayout_2.addWidget(self.outputSplit, 3, 1, 1, 1) + self.gridLayout_2.addWidget(self.mozJpegBox, 4, 0, 1, 1) - self.rotateBox = QCheckBox(self.optionWidget) - self.rotateBox.setObjectName(u"rotateBox") - self.rotateBox.setTristate(True) + self.autoLevelBox = QCheckBox(self.optionWidget) + self.autoLevelBox.setObjectName(u"autoLevelBox") - self.gridLayout_2.addWidget(self.rotateBox, 1, 1, 1, 1) - - self.borderBox = QCheckBox(self.optionWidget) - self.borderBox.setObjectName(u"borderBox") - self.borderBox.setTristate(True) - - self.gridLayout_2.addWidget(self.borderBox, 3, 0, 1, 1) - - self.maximizeStrips = QCheckBox(self.optionWidget) - self.maximizeStrips.setObjectName(u"maximizeStrips") - - self.gridLayout_2.addWidget(self.maximizeStrips, 4, 1, 1, 1) - - self.rotateFirstBox = QCheckBox(self.optionWidget) - self.rotateFirstBox.setObjectName(u"rotateFirstBox") - - self.gridLayout_2.addWidget(self.rotateFirstBox, 8, 1, 1, 1) + self.gridLayout_2.addWidget(self.autoLevelBox, 8, 2, 1, 1) self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2) @@ -533,33 +538,49 @@ class Ui_mainWindow(object): self.preserveMarginLabel.setText(QCoreApplication.translate("mainWindow", u"Preserve Margin %", None)) self.croppingPowerLabel.setText(QCoreApplication.translate("mainWindow", u"Cropping power:", None)) #if QT_CONFIG(tooltip) - self.interPanelCropBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - Disabled
Disabled

Indeterminate - Horizontal
Crop empty horizontal lines.

Checked - Both
Crop empty horizontal and vertical lines.

", None)) + self.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Disable automatic gamma correction.

", None)) #endif // QT_CONFIG(tooltip) - self.interPanelCropBox.setText(QCoreApplication.translate("mainWindow", u"Inter-panel crop", None)) + self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None)) #if QT_CONFIG(tooltip) self.mangaBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Enable right-to-left reading.

", None)) #endif // QT_CONFIG(tooltip) self.mangaBox.setText(QCoreApplication.translate("mainWindow", u"Right-to-left mode", None)) +#if QT_CONFIG(tooltip) + self.borderBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - Autodetection
The color of margins fill will be detected automatically.

Indeterminate - White
Margins will be filled with white color.

Checked - Black
Margins will be filled with black color.

", None)) +#endif // QT_CONFIG(tooltip) + self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None)) +#if QT_CONFIG(tooltip) + self.interPanelCropBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - Disabled
Disabled

Indeterminate - Horizontal
Crop empty horizontal lines.

Checked - Both
Crop empty horizontal and vertical lines.

", None)) +#endif // QT_CONFIG(tooltip) + self.interPanelCropBox.setText(QCoreApplication.translate("mainWindow", u"Inter-panel crop", None)) +#if QT_CONFIG(tooltip) + self.fileFusionBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Combines all selected files into a single file. (Helpful for combining chapters into volumes.)

", None)) +#endif // QT_CONFIG(tooltip) + self.fileFusionBox.setText(QCoreApplication.translate("mainWindow", u"File Fusion", None)) #if QT_CONFIG(tooltip) self.authorEdit.setToolTip(QCoreApplication.translate("mainWindow", u"Default Author is KCC", None)) #endif // QT_CONFIG(tooltip) self.authorEdit.setPlaceholderText(QCoreApplication.translate("mainWindow", u"Default Author", None)) #if QT_CONFIG(tooltip) - self.croppingBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - Disabled

Disabled

Indeterminate - Margins
Margins

Checked - Margins + page numbers
Margins +page numbers

", None)) + self.rotateFirstBox.setToolTip(QCoreApplication.translate("mainWindow", u"

When the spread splitter option is partially checked,

Unchecked - Rotate Last
Put the rotated 2 page spread after the split spreads.

Checked - Rotate First
Put the rotated 2 page spread before the split spreads.

", None)) #endif // QT_CONFIG(tooltip) - self.croppingBox.setText(QCoreApplication.translate("mainWindow", u"Cropping mode", None)) + self.rotateFirstBox.setText(QCoreApplication.translate("mainWindow", u"Rotate First", None)) #if QT_CONFIG(tooltip) - self.webtoonBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Enable special parsing mode for Korean Webtoons.

", None)) + self.reduceRainbowBox.setToolTip(QCoreApplication.translate("mainWindow", u"Reduce rainbow effect on color eink by slightly blurring images", None)) #endif // QT_CONFIG(tooltip) - self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None)) + self.reduceRainbowBox.setText(QCoreApplication.translate("mainWindow", u"Rainbow blur", None)) #if QT_CONFIG(tooltip) - self.colorBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Disable conversion to grayscale.

", None)) + self.chunkSizeCheckBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked
Maximal output file size is 100 MB for Webtoon, 400 MB for others before split occurs.

Checked
Output file size specified in "Chunk size MB" before split occurs.

", None)) #endif // QT_CONFIG(tooltip) - self.colorBox.setText(QCoreApplication.translate("mainWindow", u"Color mode", None)) + self.chunkSizeCheckBox.setText(QCoreApplication.translate("mainWindow", u"Chunk size", None)) #if QT_CONFIG(tooltip) - self.deleteBox.setToolTip(QCoreApplication.translate("mainWindow", u"Delete input file(s) or directory. It's not recoverable!", None)) + self.rotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - Split
Double page spreads will be cut into two separate pages.

Indeterminate - Split and rotate
Double page spreads will be displayed twice. First split and then rotated.

Checked - Rotate
Double page spreads will be rotated.

", None)) #endif // QT_CONFIG(tooltip) - self.deleteBox.setText(QCoreApplication.translate("mainWindow", u"Delete input", None)) + self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None)) +#if QT_CONFIG(tooltip) + self.outputSplit.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - Automatic mode
The output will be split automatically.

Checked - Volume mode
Every subdirectory will be considered as a separate volume.

", None)) +#endif // QT_CONFIG(tooltip) + self.outputSplit.setText(QCoreApplication.translate("mainWindow", u"Output split", None)) #if QT_CONFIG(tooltip) self.comicinfoTitleBox.setToolTip(QCoreApplication.translate("mainWindow", u"Write Title from ComicInfo.xml", None)) #endif // QT_CONFIG(tooltip) @@ -569,61 +590,49 @@ class Ui_mainWindow(object): #endif // QT_CONFIG(tooltip) self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None)) #if QT_CONFIG(tooltip) - self.reduceRainbowBox.setToolTip(QCoreApplication.translate("mainWindow", u"Reduce rainbow effect on color eink by slightly blurring images", None)) + self.spreadShiftBox.setToolTip(QCoreApplication.translate("mainWindow", u"Shift first page to opposite side in landscape for two page spread alignment", None)) #endif // QT_CONFIG(tooltip) - self.reduceRainbowBox.setText(QCoreApplication.translate("mainWindow", u"Rainbow blur", None)) -#if QT_CONFIG(tooltip) - self.noRotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"Do not rotate double page spreads in spread splitter option.", None)) -#endif // QT_CONFIG(tooltip) - self.noRotateBox.setText(QCoreApplication.translate("mainWindow", u"No rotate", None)) -#if QT_CONFIG(tooltip) - self.fileFusionBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Combines all selected files into a single file. (Helpful for combining chapters into volumes.)

", None)) -#endif // QT_CONFIG(tooltip) - self.fileFusionBox.setText(QCoreApplication.translate("mainWindow", u"File Fusion", None)) -#if QT_CONFIG(tooltip) - self.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Disable automatic gamma correction.

", None)) -#endif // QT_CONFIG(tooltip) - self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None)) -#if QT_CONFIG(tooltip) - self.mozJpegBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - JPEG
Use JPEG files

Indeterminate - force PNG
Create PNG files instead JPEG

Checked - mozJpeg
10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2

", None)) -#endif // QT_CONFIG(tooltip) - self.mozJpegBox.setText(QCoreApplication.translate("mainWindow", u"JPEG/PNG/mozJpeg", None)) + self.spreadShiftBox.setText(QCoreApplication.translate("mainWindow", u"Spread shift", None)) #if QT_CONFIG(tooltip) self.disableProcessingBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Do not process any image, ignore profile and processing options.

", None)) #endif // QT_CONFIG(tooltip) self.disableProcessingBox.setText(QCoreApplication.translate("mainWindow", u"Disable processing", None)) #if QT_CONFIG(tooltip) - self.chunkSizeCheckBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked
Maximal output file size is 100 MB for Webtoon, 400 MB for others before split occurs.

Checked
Output file size specified in "Chunk size MB" before split occurs.

", None)) + self.webtoonBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Enable special parsing mode for Korean Webtoons.

", None)) #endif // QT_CONFIG(tooltip) - self.chunkSizeCheckBox.setText(QCoreApplication.translate("mainWindow", u"Chunk size", None)) + self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None)) #if QT_CONFIG(tooltip) - self.spreadShiftBox.setToolTip(QCoreApplication.translate("mainWindow", u"Shift first page to opposite side in landscape for two page spread alignment", None)) + self.colorBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Disable conversion to grayscale.

", None)) #endif // QT_CONFIG(tooltip) - self.spreadShiftBox.setText(QCoreApplication.translate("mainWindow", u"Spread shift", None)) + self.colorBox.setText(QCoreApplication.translate("mainWindow", u"Color mode", None)) #if QT_CONFIG(tooltip) - self.upscaleBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - Nothing
Images smaller than device resolution will not be resized.

Indeterminate - Stretching
Images smaller than device resolution will be resized. Aspect ratio will be not preserved.

Checked - Upscaling
Images smaller than device resolution will be resized. Aspect ratio will be preserved.

", None)) + self.croppingBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - Disabled

Disabled

Indeterminate - Margins
Margins

Checked - Margins + page numbers
Margins +page numbers

", None)) #endif // QT_CONFIG(tooltip) - self.upscaleBox.setText(QCoreApplication.translate("mainWindow", u"Stretch/Upscale", None)) -#if QT_CONFIG(tooltip) - self.outputSplit.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - Automatic mode
The output will be split automatically.

Checked - Volume mode
Every subdirectory will be considered as a separate volume.

", None)) -#endif // QT_CONFIG(tooltip) - self.outputSplit.setText(QCoreApplication.translate("mainWindow", u"Output split", None)) -#if QT_CONFIG(tooltip) - self.rotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - Split
Double page spreads will be cut into two separate pages.

Indeterminate - Split and rotate
Double page spreads will be displayed twice. First split and then rotated.

Checked - Rotate
Double page spreads will be rotated.

", None)) -#endif // QT_CONFIG(tooltip) - self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None)) -#if QT_CONFIG(tooltip) - self.borderBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - Autodetection
The color of margins fill will be detected automatically.

Indeterminate - White
Margins will be filled with white color.

Checked - Black
Margins will be filled with black color.

", None)) -#endif // QT_CONFIG(tooltip) - self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None)) + self.croppingBox.setText(QCoreApplication.translate("mainWindow", u"Cropping mode", None)) #if QT_CONFIG(tooltip) self.maximizeStrips.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - 1x4
Keep format 1x4 panels strips.

Checked - 2x2
Turn 1x4 strips to 2x2 to maximize screen usage.

", None)) #endif // QT_CONFIG(tooltip) self.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None)) #if QT_CONFIG(tooltip) - self.rotateFirstBox.setToolTip(QCoreApplication.translate("mainWindow", u"

When the spread splitter option is partially checked,

Unchecked - Rotate Last
Put the rotated 2 page spread after the split spreads.

Checked - Rotate First
Put the rotated 2 page spread before the split spreads.

", None)) + self.noRotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"Do not rotate double page spreads in spread splitter option.", None)) #endif // QT_CONFIG(tooltip) - self.rotateFirstBox.setText(QCoreApplication.translate("mainWindow", u"Rotate First", None)) + self.noRotateBox.setText(QCoreApplication.translate("mainWindow", u"No rotate", None)) +#if QT_CONFIG(tooltip) + self.deleteBox.setToolTip(QCoreApplication.translate("mainWindow", u"Delete input file(s) or directory. It's not recoverable!", None)) +#endif // QT_CONFIG(tooltip) + self.deleteBox.setText(QCoreApplication.translate("mainWindow", u"Delete input", None)) +#if QT_CONFIG(tooltip) + self.upscaleBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - Nothing
Images smaller than device resolution will not be resized.

Indeterminate - Stretching
Images smaller than device resolution will be resized. Aspect ratio will be not preserved.

Checked - Upscaling
Images smaller than device resolution will be resized. Aspect ratio will be preserved.

", None)) +#endif // QT_CONFIG(tooltip) + self.upscaleBox.setText(QCoreApplication.translate("mainWindow", u"Stretch/Upscale", None)) +#if QT_CONFIG(tooltip) + self.mozJpegBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Unchecked - JPEG
Use JPEG files

Indeterminate - force PNG
Create PNG files instead JPEG

Checked - mozJpeg
10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2

", None)) +#endif // QT_CONFIG(tooltip) + self.mozJpegBox.setText(QCoreApplication.translate("mainWindow", u"JPEG/PNG/mozJpeg", None)) +#if QT_CONFIG(tooltip) + self.autoLevelBox.setToolTip(QCoreApplication.translate("mainWindow", u"

Set the most common dark pixel value to be the black point for leveling on a page by page basis.

Skipped for any images that were originally color.

Useful for very gray faded content. Reccomended to use with Custom Gamma = 1.0 (Disabled).

", None)) +#endif // QT_CONFIG(tooltip) + self.autoLevelBox.setText(QCoreApplication.translate("mainWindow", u"Aggressive Black Point", None)) self.gammaLabel.setText(QCoreApplication.translate("mainWindow", u"Gamma: Auto", None)) #if QT_CONFIG(tooltip) self.chunkSizeWidget.setToolTip(QCoreApplication.translate("mainWindow", u"

Warning: chunk size greater than default may cause
performance/battery issues, especially on older devices.

", None)) diff --git a/kindlecomicconverter/comic2ebook.py b/kindlecomicconverter/comic2ebook.py index d82ccc0..850e47f 100755 --- a/kindlecomicconverter/comic2ebook.py +++ b/kindlecomicconverter/comic2ebook.py @@ -1138,6 +1138,8 @@ def makeParser(): help="Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0]") processing_options.add_argument("-g", "--gamma", type=float, dest="gamma", default="0.0", help="Apply gamma correction to linearize the image [Default=Auto]") + output_options.add_argument("--autolevel", action="store_true", dest="autolevel", default=False, + help="Set most common dark pixel value to be black point for leveling.") processing_options.add_argument("-c", "--cropping", type=int, dest="cropping", default="2", help="Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]") processing_options.add_argument("--cp", "--croppingpower", type=float, dest="croppingp", default="1.0", diff --git a/kindlecomicconverter/image.py b/kindlecomicconverter/image.py index 73ee63a..3dc47c5 100755 --- a/kindlecomicconverter/image.py +++ b/kindlecomicconverter/image.py @@ -353,6 +353,18 @@ class ComicPage: self.image = Image.eval(self.image, lambda a: int(255 * (a / 255.) ** gamma)) def autocontrastImage(self): + if self.opt.autolevel and not self.color: + self.convertToGrayscale() + h = self.image.histogram() + most_common_dark_pixel_count = max(h[:64]) + black_point = h.index(most_common_dark_pixel_count) + bp = black_point + self.image = self.image.point(lambda p: p if p > bp else bp) + + # don't autocontrast grayscale pages that were originally color + if not self.opt.forcecolor and self.color: + return + self.image = ImageOps.autocontrast(self.image, preserve_tone=True) def convertToGrayscale(self):