Squash commits
|
@ -0,0 +1,2 @@
|
|||
.hugo_build.lock
|
||||
public
|
|
@ -0,0 +1,30 @@
|
|||
# Обновленный TheDroth.Rocks
|
||||
## Подготовка
|
||||
- Установить Docker
|
||||
- Загрузить образ с [Hugo](https://hub.docker.com/r/klakegg/hugo):
|
||||
```
|
||||
docker pull klakegg/hugo
|
||||
```
|
||||
## Запуск
|
||||
- Перейти в корневой каталог с проектом
|
||||
- Выполнить запуск Hugo сервера
|
||||
1) Для тестов:
|
||||
```
|
||||
docker run --rm -v $(pwd):/src -p 1313:1313 klakegg/hugo server
|
||||
```
|
||||
2) Для прода:
|
||||
|
||||
```
|
||||
docker run -v $(pwd):/src -p 1313:1313 klakegg/hugo
|
||||
```
|
||||
## Мини FAQ
|
||||
- ```config.yml``` - основной файл настройки сайта.
|
||||
- Записи (странички) складываются в ```content/core``` и имеют ```.md``` формат.
|
||||
- Если нужно, чтобы страницы не отображались на главном экране - расположите их в директории отличной от ```content/core```. Например, для блога можно использовать ```content/blog```.
|
||||
- Картинки лежат в ```static```.
|
||||
|
||||
## Документация
|
||||
- [Hugo Docker](https://hub.docker.com/r/klakegg/hugo)
|
||||
- [Hugo Docs](https://gohugo.io/documentation/)
|
||||
- [Template Repo](https://github.com/adityatelange/hugo-PaperMod)
|
||||
- [Template miniFAQ](https://adityatelange.github.io/hugo-PaperMod/posts/papermod/papermod-faq/)
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: "{{ replace .Name "-" " " | title }}"
|
||||
date: {{ .Date }}
|
||||
draft: true
|
||||
---
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
baseURL = 'http://example.org/'
|
||||
languageCode = 'en-us'
|
||||
title = 'My New Hugo Site'
|
|
@ -0,0 +1,148 @@
|
|||
baseURL: "https://thedroth.rocks"
|
||||
title: TheDrothRocks!
|
||||
paginate: 5
|
||||
theme: PaperMod
|
||||
|
||||
enableRobotsTXT: true
|
||||
buildDrafts: false
|
||||
buildFuture: false
|
||||
buildExpired: false
|
||||
|
||||
# googleAnalytics: UA-123-45
|
||||
|
||||
minify:
|
||||
disableXML: true
|
||||
minifyOutput: true
|
||||
|
||||
params:
|
||||
env: production # to enable google analytics, opengraph, twitter-cards and schema.
|
||||
title: TheDroth Rocks!
|
||||
description: "TheДроты рулят"
|
||||
keywords: [Blog, Portfolio, PaperMod]
|
||||
author: ["nothing", "sn4il"]
|
||||
# author: ["Me", "You"] # multiple authors
|
||||
images: ["<link or path of image for opengraph, twitter-cards>"]
|
||||
DateFormat: "January 2, 2006"
|
||||
defaultTheme: auto # dark, light
|
||||
disableThemeToggle: false
|
||||
|
||||
ShowReadingTime: true
|
||||
ShowShareButtons: true
|
||||
ShowPostNavLinks: true
|
||||
ShowBreadCrumbs: true
|
||||
ShowCodeCopyButtons: false
|
||||
ShowWordCount: true
|
||||
ShowRssButtonInSectionTermList: true
|
||||
UseHugoToc: true
|
||||
disableSpecial1stPost: false
|
||||
disableScrollToTop: false
|
||||
comments: false
|
||||
hidemeta: false
|
||||
hideSummary: false
|
||||
showtoc: false
|
||||
tocopen: false
|
||||
params:
|
||||
# mainSections:
|
||||
# - core
|
||||
# - blog
|
||||
|
||||
|
||||
assets:
|
||||
# disableHLJS: true # to disable highlight.js
|
||||
# disableFingerprinting: true
|
||||
favicon: "/images/favicon.png"
|
||||
favicon16x16: "/images/favicon.png"
|
||||
favicon32x32: "/images/favicon.png"
|
||||
apple_touch_icon: "<link / abs url>"
|
||||
safari_pinned_tab: "<link / abs url>"
|
||||
|
||||
label:
|
||||
text: "TheDroth.Rocks"
|
||||
icon: "/images/thedroth_logo.png"
|
||||
iconHeight: 35
|
||||
|
||||
# profile-mode
|
||||
profileMode:
|
||||
enabled: false # needs to be explicitly set
|
||||
title: ExampleSite
|
||||
subtitle: "This is subtitle"
|
||||
imageUrl: "<img location>"
|
||||
imageWidth: 120
|
||||
imageHeight: 120
|
||||
imageTitle: my image
|
||||
buttons:
|
||||
- name: Posts
|
||||
url: posts
|
||||
- name: Tags
|
||||
url: tags
|
||||
|
||||
# home-info mode
|
||||
homeInfoParams:
|
||||
Title: "Привет всем TheDroth'ам \U0001F44B"
|
||||
Content: Добро пожаловать на наш сайт!
|
||||
|
||||
socialIcons:
|
||||
- name: lemmy
|
||||
url: "https://asocial.thedroth.rocks/"
|
||||
- name: gitea
|
||||
url: "https://git.thedroth.rocks/TheDroth/thedroth-rocks"
|
||||
- name: piped
|
||||
url: "https://piped.thedroth.rocks/"
|
||||
|
||||
|
||||
analytics:
|
||||
google:
|
||||
SiteVerificationTag: "XYZabc"
|
||||
bing:
|
||||
SiteVerificationTag: "XYZabc"
|
||||
yandex:
|
||||
SiteVerificationTag: "XYZabc"
|
||||
|
||||
cover:
|
||||
hidden: true # hide everywhere but not in structured data
|
||||
hiddenInList: true # hide on list pages and home
|
||||
hiddenInSingle: true # hide on single page
|
||||
|
||||
# editPost:
|
||||
# URL: "https://github.com/<path_to_repo>/content"
|
||||
# Text: "Suggest Changes" # edit text
|
||||
# appendFilePath: true # to append file path to Edit link
|
||||
|
||||
# for search
|
||||
# https://fusejs.io/api/options.html
|
||||
fuseOpts:
|
||||
isCaseSensitive: false
|
||||
shouldSort: true
|
||||
location: 0
|
||||
distance: 1000
|
||||
threshold: 0.4
|
||||
minMatchCharLength: 0
|
||||
keys: ["title", "permalink", "summary", "content"]
|
||||
menu:
|
||||
main:
|
||||
- identifier: our-services
|
||||
name: Наши сервисы
|
||||
url: /core/our-services/
|
||||
weight: 10
|
||||
- identifier: proxy
|
||||
name: Проксирующие сервисы
|
||||
url: /core/proxy/
|
||||
weight: 20
|
||||
- identifier: tos
|
||||
name: ToS
|
||||
url: https://git.thedroth.rocks/TheDroth/ToS/src/branch/main/README.md
|
||||
weight: 30
|
||||
- identifier: blog
|
||||
name: Блог
|
||||
url: /tags/blog/
|
||||
weight: 40
|
||||
# Read: https://github.com/adityatelange/hugo-PaperMod/wiki/FAQs#using-hugos-syntax-highlighter-chroma
|
||||
pygmentsUseClasses: true
|
||||
markup:
|
||||
highlight:
|
||||
noClasses: false
|
||||
# anchorLineNos: true
|
||||
# codeFences: true
|
||||
# guessSyntax: true
|
||||
# lineNos: true
|
||||
# style: monokai
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
title: "Редизайн"
|
||||
date: 2023-10-26T08:30:03+03:00
|
||||
weight: 2
|
||||
tags: ["blog"]
|
||||
author: "sn4il"
|
||||
showToc: false
|
||||
TocOpen: false
|
||||
draft: false
|
||||
hidemeta: false
|
||||
comments: false
|
||||
description: "Все сервисы, которые мы проксируем. Заголовки - ссылки."
|
||||
disableHLJS: true # to disable highlightjs
|
||||
disableShare: false
|
||||
disableHLJS: false
|
||||
hideSummary: false
|
||||
searchHidden: true
|
||||
ShowReadingTime: false
|
||||
ShowBreadCrumbs: true
|
||||
ShowPostNavLinks: true
|
||||
ShowWordCount: false
|
||||
ShowRssButtonInSectionTermList: true
|
||||
UseHugoToc: true
|
||||
---
|
||||
|
||||
# Смена дизайна
|
||||
|
||||
Благодаря усилиям товарища Nothing, сайт переведён с олдскульного статического HTML на Hugo. Теперь мы не только симпатично смотримся, но и можем вести блог. Здесь будут публиковаться изменения и планы касательно сервисов и дальнейшего развития проекта. И, возможно, внутренние новости от команды.
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
title: "Donate!"
|
||||
date: 2023-10-26T09:00:03+03:00
|
||||
weight: 3
|
||||
# aliases: ["/first"]
|
||||
#tags: ["donae"]
|
||||
author: "sn4il"
|
||||
# author: ["Me", "You"] # multiple authors
|
||||
showToc: true
|
||||
TocOpen: false
|
||||
draft: false
|
||||
hidemeta: false
|
||||
comments: false
|
||||
description: "Поддержка - это хорошо, а материальная поддержка - всегда спасибо!"
|
||||
disableHLJS: true # to disable highlightjs
|
||||
disableShare: false
|
||||
disableHLJS: false
|
||||
hideSummary: false
|
||||
searchHidden: true
|
||||
ShowReadingTime: false
|
||||
ShowBreadCrumbs: true
|
||||
ShowPostNavLinks: true
|
||||
ShowWordCount: false
|
||||
ShowRssButtonInSectionTermList: true
|
||||
UseHugoToc: true
|
||||
---
|
||||
Больше спасибо, что посетили данную страницу! Обращаем ваше внимание, что материальная поддержка вовсе не обязательна и вы можете пользоваться всеми нашими ресурсами абсолютно бесплатно!
|
||||
## Способы поддержки
|
||||
### Единоразово
|
||||
- Банковская карта:
|
||||
```
|
||||
номер карты
|
||||
```
|
||||
- Донат с сообщением: [Donationalerts](), [Boosty]()
|
||||
|
||||
- Криптовалюта:
|
||||
- btc:
|
||||
```
|
||||
номер кошелька
|
||||
```
|
||||
### Подписка
|
||||
|
||||
Подписаться можно на [Boosty]()
|
||||
|
||||
## Зачем мне Вас поддерживать?
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
title: "Наши сервисы"
|
||||
date: 2023-10-26T09:00:03+03:00
|
||||
weight: 1
|
||||
# aliases: ["/first"]
|
||||
tags: ["first"]
|
||||
# author: "nothing"
|
||||
author: ["nothing", "sn4il"] # multiple authors
|
||||
showToc: true
|
||||
TocOpen: false
|
||||
draft: false
|
||||
hidemeta: false
|
||||
comments: false
|
||||
description: "Наши собственные сервисы."
|
||||
disableHLJS: true # to disable highlightjs
|
||||
disableShare: false
|
||||
disableHLJS: false
|
||||
hideSummary: false
|
||||
searchHidden: true
|
||||
ShowReadingTime: true
|
||||
ShowBreadCrumbs: true
|
||||
ShowPostNavLinks: true
|
||||
ShowWordCount: true
|
||||
ShowRssButtonInSectionTermList: true
|
||||
UseHugoToc: true
|
||||
---
|
||||
Список сервисов, расположенных на серверах TheDroth. Все сервисы из данного списка "подчиняются" нашему [ToS]([https](https://git.thedroth.rocks/TheDroth/ToS/src/branch/main/README.md)), а потому настоятельно рекомендуем ознакомиться с ним перед началом взаимодействия с ресурсами.
|
||||
|
||||
## [Asocial](https://asocial.thedroth.rocks)
|
||||
|
||||
[](https://asocial.thedroth.rocks/)
|
||||
|
||||
Lemmy - федеративный форум-аггрегатор ссылок в стиле Reddit
|
||||
|
||||
## [TheДротский поиск SearXNG](https://search.thedroth.rocks/)
|
||||
|
||||
[](https://search.thedroth.rocks/)
|
||||
|
||||
Метапоисковый сервис (поиск через Google, Bing, DuckDuckGo...)
|
||||
|
||||
## [Gitea](https://git.thedroth.rocks)
|
||||
|
||||
[](https://git.thedroth.rocks/)
|
||||
|
||||
Хранилище исходных кодов
|
||||
|
||||
## [RSS Bridge](https://rss.thedroth.rocks/)
|
||||
|
||||
[](https://rss.thedroth.rocks/)
|
||||
|
||||
Конвертация новостных лент в RSS
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
title: "Проксирующие сервисы"
|
||||
date: 2023-10-26T09:00:03+03:00
|
||||
weight: 2
|
||||
# aliases: ["/first"]
|
||||
#tags: ["first"]
|
||||
#author: "nothing"
|
||||
author: ["nothing", "sn4il"] # multiple authors
|
||||
showToc: false
|
||||
TocOpen: false
|
||||
draft: false
|
||||
hidemeta: false
|
||||
comments: false
|
||||
description: "Все сервисы, которые мы проксируем."
|
||||
disableHLJS: true # to disable highlightjs
|
||||
disableShare: false
|
||||
disableHLJS: false
|
||||
hideSummary: false
|
||||
searchHidden: true
|
||||
ShowReadingTime: false
|
||||
ShowBreadCrumbs: true
|
||||
ShowPostNavLinks: true
|
||||
ShowWordCount: false
|
||||
ShowRssButtonInSectionTermList: true
|
||||
UseHugoToc: true
|
||||
---
|
||||
Список прокси — альтернативных фронтэндов для централизованных сервисов (Youtube, Wikipedia...), облегчающих взаимодействие с последними.
|
||||
|
||||
|
||||
## [Piped](https://piped.thedroth.rocks/)
|
||||
|
||||
[](https://piped.thedroth.rocks/)
|
||||
|
||||
Лёгкий приватный интерфейс для YouTube, умеюший автоматически проматывать рекламные вставки. Зачем кормить гугл, если можно не кормить...
|
||||
|
||||
## [WikiLess](https://wiki.thedroth.rocks/)
|
||||
|
||||
[](https://wiki.thedroth.rocks)
|
||||
|
||||
Wikipedia без ненужных элементов и трекеров
|
||||
|
||||
## [Mozhi](https://tl.thedroth.rocks/)
|
||||
|
||||
[
|
||||
](https://tl.thedroth.rocks/)
|
||||
|
||||
|
||||
Интерфейс для множества сервисов-переводчиков
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
title: "Наши сервисы" # Название материала
|
||||
date: 2020-09-15T11:30:03+00:00 # Дата создания материала
|
||||
weight: 1 # "Важность материала. Чем ниже 'важность' - тем выше в списке будет находится"
|
||||
# aliases: ["/first"] #
|
||||
tags: ["first"] # теги. Необходимо материалы помечать по тегам для того, чтобы проще было находить нужный матриеал
|
||||
author: "nothing" # один автор материала
|
||||
author: ["nothing", "sn4il"] # много авторов материала
|
||||
showToc: true # оглавление загаловков в материале. True - показывать заголовки в материале
|
||||
TocOpen: true # оглавление загаловков в материале. True - показывать заголовки в материале
|
||||
draft: true # "черновик". True - материал виден публично и считается опубликованным. False - его не видно.
|
||||
hidemeta: false #
|
||||
comments: false # Комментарии
|
||||
description: "Наши собственные сервисы. Заголовки - ссылки." # Небольшая аннотация под заголовком
|
||||
disableHLJS: true # to disable highlightjs
|
||||
disableShare: false # иконки "поделиться" внизу материала. True - иконок не будет.
|
||||
disableHLJS: false #
|
||||
hideSummary: true # Описание материала до входа на сам материал. True - скрыть описание, оставить только заголовок.
|
||||
searchHidden: true #
|
||||
ShowReadingTime: true # Приблизительное время на чтение. True - показывать.
|
||||
ShowBreadCrumbs: true #
|
||||
ShowPostNavLinks: true # Кнопки "другой материал" внизу материала. True - показывать кнопки.
|
||||
ShowWordCount: true # Количество слов в материале. True - показывать кол-во слов.
|
||||
ShowRssButtonInSectionTermList: true #
|
||||
UseHugoToc: false #
|
||||
---
|
186
dither.py
|
@ -1,186 +0,0 @@
|
|||
#!/usr/local/bin/python3.8
|
||||
|
||||
import hitherdither
|
||||
import os
|
||||
import argparse
|
||||
import shutil
|
||||
from PIL import Image
|
||||
import logging
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
"""
|
||||
This script recursively traverses folders and creates dithered versions of the images it finds.
|
||||
These are stored in the same folder as the images in a folder called "dithers".
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-d', '--directory', help="Set the directory to traverse", default="."
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-rm', '--remove', help="Removes all the folders with dithers and their contents", action="store_true"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-c', '--colorize', help="Colorizes the dithered images", action="store_true"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-v', '--verbose', help="Print out more detailed information about what this script is doing", action="store_true"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
image_ext = [".jpg", ".JPG", ".jpeg", ".png", ".gif", ".webp", ".tiff", ".bmp"]
|
||||
|
||||
|
||||
content_dir = args.directory
|
||||
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
exclude_dirs = set(["dithers"])
|
||||
|
||||
|
||||
logging.info("Dithering all images in {} and subfolders".format(content_dir))
|
||||
logging.debug("excluding directories: {}".format("".join(exclude_dirs)))
|
||||
|
||||
def colorize(source_image, category):
|
||||
"""
|
||||
Picks a colored dithering palette based on the post category.
|
||||
"""
|
||||
|
||||
colors = {
|
||||
'low-tech': hitherdither.palette.Palette([(30,32,40), (11,21,71),(57,77,174),(158,168,218),(187,196,230),(243,244,250)]),
|
||||
'obsolete': hitherdither.palette.Palette([(9,74,58), (58,136,118),(101,163,148),(144,189,179),(169,204,195),(242,247,246)]),
|
||||
'high-tech': hitherdither.palette.Palette([(86,9,6), (197,49,45),(228,130,124),(233,155,151),(242,193,190),(252,241,240)]),
|
||||
'grayscale': hitherdither.palette.Palette([(25,25,25), (75,75,75),(125,125,125),(175,175,175),(225,225,225),(250,250,250)])
|
||||
}
|
||||
|
||||
|
||||
if category:
|
||||
|
||||
for i in colors.keys():
|
||||
if i in category.lower():
|
||||
color = colors[i]
|
||||
logging.info("Applying color palette '{}' for {}".format(i, category))
|
||||
break
|
||||
else:
|
||||
logging.info("No category for {}, {}".format(source_image, category))
|
||||
print("No category for {}, {}".format(source_image, category))
|
||||
color = colors['grayscale']
|
||||
|
||||
else:
|
||||
logging.info("No category for {}, {}".format(source_image, category))
|
||||
print("No category for {}, {}".format(source_image, category))
|
||||
color = colors['grayscale']
|
||||
|
||||
return color
|
||||
|
||||
|
||||
def dither_image(source_image, output_image, category ='high-tech'):
|
||||
#see hitherdither docs for different dithering algos and settings
|
||||
|
||||
# if args.colorize:
|
||||
# palette = colorize(source_image, category)
|
||||
# else:
|
||||
# palette = hitherdither.palette.Palette([(25,25,25), (75,75,75),(125,125,125),(175,175,175),(225,225,225),(250,250,250)])
|
||||
try:
|
||||
img= Image.open(source_image).convert('RGB')
|
||||
img.thumbnail((800,800), Image.LANCZOS)
|
||||
#palette = palettes[category]
|
||||
#palette = hitherdither.palette.Palette.create_by_median_cut(img)
|
||||
palette = hitherdither.palette.Palette(
|
||||
[0x080000, 0x201A0B, 0x432817, 0x492910,
|
||||
0x234309, 0x5D4F1E, 0x9C6B20, 0xA9220F,
|
||||
0x2B347C, 0x2B7409, 0xD0CA40, 0xE8A077,
|
||||
0x6A94AB, 0xD5C4B3, 0xFCE76E, 0xFCFAE2]
|
||||
)
|
||||
threshold = [96, 96, 96]
|
||||
img_dithered = hitherdither.ordered.bayer.bayer_dithering(img, palette, threshold, order=8)
|
||||
#if args.colorize:
|
||||
# img_dithered = colorize(img_dithered, category)
|
||||
# logging.debug("Created {} in category {}".format(img_dithered, category))
|
||||
|
||||
img_dithered.save(output_image, optimize=True)
|
||||
|
||||
except Exception as e:
|
||||
logging.debug(" failed to convert {}".format(source_image))
|
||||
logging.debug(e)
|
||||
|
||||
def delete_dithers(content_dir):
|
||||
logging.info("Deleting 'dither' folders in {} and below".format(content_dir))
|
||||
for root, dirs, files in os.walk(content_dir, topdown=True):
|
||||
if root.endswith('dithers'):
|
||||
shutil.rmtree(root)
|
||||
logging.info("Removed {}".format(root))
|
||||
|
||||
|
||||
def parse_front_matter(md):
|
||||
with open(md) as f:
|
||||
contents = f.readlines()
|
||||
cat = None
|
||||
for l in contents:
|
||||
if l.startswith("categories: "):
|
||||
cat = l.split("categories: ")[1]
|
||||
cat = cat.strip("[")
|
||||
cat = cat.strip()
|
||||
cat = cat.strip("]")
|
||||
|
||||
logging.debug("Categories: {} from {}".format(cat, l.strip()))
|
||||
return cat
|
||||
|
||||
prev_root = None
|
||||
|
||||
if args.remove:
|
||||
delete_dithers(
|
||||
os.path.abspath(content_dir)
|
||||
)
|
||||
else:
|
||||
for root, dirs, files in os.walk(os.path.abspath(content_dir), topdown=True):
|
||||
logging.debug("Checking next folder {}".format(root))
|
||||
|
||||
dirs[:] = [d for d in dirs if d not in exclude_dirs]
|
||||
|
||||
category = None
|
||||
if prev_root is None:
|
||||
prev_root = root
|
||||
|
||||
if prev_root is not root:
|
||||
if files:
|
||||
if any(x.endswith(tuple(image_ext)) for x in files):
|
||||
if not os.path.exists(os.path.join(root,'dithers')):
|
||||
os.mkdir(os.path.join(root,'dithers'))
|
||||
logging.info(" created in {}".format(root))
|
||||
|
||||
if args.colorize:
|
||||
#iterate over md files to find one with a category
|
||||
if not category:
|
||||
for i in os.listdir(root):
|
||||
if i.startswith('index'):
|
||||
category2 = parse_front_matter(os.path.join(root,i))
|
||||
|
||||
break
|
||||
|
||||
|
||||
for fname in files:
|
||||
if fname.endswith(tuple(image_ext)):
|
||||
file_, ext = os.path.splitext(fname)
|
||||
source_image= os.path.join(root,fname)
|
||||
output_image = os.path.join(os.path.join(root, 'dithers'), file_+'_dithered.png')
|
||||
if not os.path.exists(output_image):
|
||||
if not args.colorize:
|
||||
category2 = "high-tech"
|
||||
dither_image(source_image,output_image, category2)
|
||||
logging.info(" converted {}".format(fname))
|
||||
logging.debug(output_image)
|
||||
else:
|
||||
logging.debug("Dithered version of {} found, skipping".format(fname))
|
||||
|
||||
prev_root = root
|
||||
|
||||
|
||||
logging.info("Done dithering")
|
Before Width: | Height: | Size: 7.9 KiB |
BIN
dithers/fonk.png
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 24 KiB |
BIN
dithers/rd.png
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 23 KiB |
BIN
favicon.png
Before Width: | Height: | Size: 69 KiB |
BIN
fonts/exo2.ttf
BIN
fonts/exo2l.otf
|
@ -1,15 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
from . import data
|
||||
from . import math
|
||||
from . import ordered
|
||||
from . import diffusion
|
||||
from . import palette
|
||||
from . import utils
|
||||
from .__version__ import __version__, version
|
|
@ -1,17 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
__version__.py
|
||||
-----------
|
||||
|
||||
:copyright: 2017-05-10 by hbldh <henrik.blidh@nedomkull.com>
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Version information.
|
||||
__version__ = "0.1.7"
|
||||
version = __version__ # backwards compatibility name
|
|
@ -1,89 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
try:
|
||||
import pathlib2 as pathlib
|
||||
except ImportError:
|
||||
import pathlib
|
||||
|
||||
try:
|
||||
from urllib import urlopen
|
||||
except ImportError:
|
||||
from urllib.request import urlopen
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
||||
def scene():
|
||||
"""Chrono Cross PNG image used in Yliluoma's web page.
|
||||
|
||||
:return: The PIL image of the Chrono Cross scene.
|
||||
|
||||
"""
|
||||
image_path = pathlib.Path(__file__).resolve().parent.joinpath("scene.png")
|
||||
image_url = "http://bisqwit.iki.fi/jutut/kuvat/ordered_dither/scene.png"
|
||||
return _image(image_path, image_url)
|
||||
|
||||
|
||||
def scene_undithered():
|
||||
"""Chrono Cross PNG image rendered directly with specified palette.
|
||||
|
||||
:return: The PIL image of the undithered Chrono Cross scene.
|
||||
|
||||
"""
|
||||
return _image(
|
||||
pathlib.Path(__file__).resolve().parent.joinpath("scenenodither.png"),
|
||||
"http://bisqwit.iki.fi/jutut/kuvat/ordered_dither/scenenodither.png",
|
||||
)
|
||||
|
||||
|
||||
def scene_bayer0():
|
||||
"""Chrono Cross PNG image dithered using ordered Bayer matrix method.
|
||||
|
||||
:return: The PIL image of the ordered Bayer matrix dithered
|
||||
Chrono Cross scene.
|
||||
|
||||
"""
|
||||
return _image(
|
||||
pathlib.Path(__file__).resolve().parent.joinpath("scenebayer0.png"),
|
||||
"http://bisqwit.iki.fi/jutut/kuvat/ordered_dither/scenebayer0.png",
|
||||
)
|
||||
|
||||
|
||||
def _image(pth, url):
|
||||
"""Load image specified in ``path``. If not present,
|
||||
fetch it from ``url`` and store locally.
|
||||
|
||||
:param str or :class:`~pathlib.Path` pth:
|
||||
:param str url: URL from where to fetch the image.
|
||||
:return: The :class:`~PIL.Image` requested.
|
||||
|
||||
"""
|
||||
if pth.exists():
|
||||
return Image.open(str(pth))
|
||||
else:
|
||||
r = urlopen(url)
|
||||
with open(str(pth), "wb") as f:
|
||||
f.write(r.read())
|
||||
return _image(pth, url)
|
||||
|
||||
|
||||
def palette():
|
||||
return [
|
||||
0x080000,
|
||||
0x201A0B,
|
||||
0x432817,
|
||||
0x492910,
|
||||
0x234309,
|
||||
0x5D4F1E,
|
||||
0x9C6B20,
|
||||
0xA9220F,
|
||||
0x2B347C,
|
||||
0x2B7409,
|
||||
0xD0CA40,
|
||||
0xE8A077,
|
||||
0x6A94AB,
|
||||
0xD5C4B3,
|
||||
0xFCE76E,
|
||||
0xFCFAE2,
|
||||
]
|
|
@ -1,193 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
:mod:`diffusion`
|
||||
=======================
|
||||
|
||||
.. moduleauthor:: hbldh <henrik.blidh@swedwise.com>
|
||||
Created on 2016-09-12, 11:34
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
import numpy as np
|
||||
|
||||
_DIFFUSION_MAPS = {
|
||||
"floyd-steinberg": (
|
||||
(1, 0, 7 / 16),
|
||||
(-1, 1, 3 / 16),
|
||||
(0, 1, 5 / 16),
|
||||
(1, 1, 1 / 16),
|
||||
),
|
||||
"atkinson": (
|
||||
(1, 0, 1 / 8),
|
||||
(2, 0, 1 / 8),
|
||||
(-1, 1, 1 / 8),
|
||||
(0, 1, 1 / 8),
|
||||
(1, 1, 1 / 8),
|
||||
(0, 2, 1 / 8),
|
||||
),
|
||||
"jarvis-judice-ninke": (
|
||||
(1, 0, 7 / 48),
|
||||
(2, 0, 5 / 48),
|
||||
(-2, 1, 3 / 48),
|
||||
(-1, 1, 5 / 48),
|
||||
(0, 1, 7 / 48),
|
||||
(1, 1, 5 / 48),
|
||||
(2, 1, 3 / 48),
|
||||
(-2, 2, 1 / 48),
|
||||
(-1, 2, 3 / 48),
|
||||
(0, 2, 5 / 48),
|
||||
(1, 2, 3 / 48),
|
||||
(2, 2, 1 / 48),
|
||||
),
|
||||
"stucki": (
|
||||
(1, 0, 8 / 42),
|
||||
(2, 0, 4 / 42),
|
||||
(-2, 1, 2 / 42),
|
||||
(-1, 1, 4 / 42),
|
||||
(0, 1, 8 / 42),
|
||||
(1, 1, 4 / 42),
|
||||
(2, 1, 2 / 42),
|
||||
(-2, 2, 1 / 42),
|
||||
(-1, 2, 2 / 42),
|
||||
(0, 2, 4 / 42),
|
||||
(1, 2, 2 / 42),
|
||||
(2, 2, 1 / 42),
|
||||
),
|
||||
"burkes": (
|
||||
(1, 0, 8 / 32),
|
||||
(2, 0, 4 / 32),
|
||||
(-2, 1, 2 / 32),
|
||||
(-1, 1, 4 / 32),
|
||||
(0, 1, 8 / 32),
|
||||
(1, 1, 4 / 32),
|
||||
(2, 1, 2 / 32),
|
||||
),
|
||||
"sierra3": (
|
||||
(1, 0, 5 / 32),
|
||||
(2, 0, 3 / 32),
|
||||
(-2, 1, 2 / 32),
|
||||
(-1, 1, 4 / 32),
|
||||
(0, 1, 5 / 32),
|
||||
(1, 1, 4 / 32),
|
||||
(2, 1, 2 / 32),
|
||||
(-1, 2, 2 / 32),
|
||||
(0, 2, 3 / 32),
|
||||
(1, 2, 2 / 32),
|
||||
),
|
||||
"sierra2": (
|
||||
(1, 0, 4 / 16),
|
||||
(2, 0, 3 / 16),
|
||||
(-2, 1, 1 / 16),
|
||||
(-1, 1, 2 / 16),
|
||||
(0, 1, 3 / 16),
|
||||
(1, 1, 2 / 16),
|
||||
(2, 1, 1 / 16),
|
||||
),
|
||||
"sierra-2-4a": (
|
||||
(1, 0, 2 / 4),
|
||||
(-1, 1, 1 / 4),
|
||||
(0, 1, 1 / 4),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def error_diffusion_dithering(image, palette, method="floyd-steinberg", order=2):
|
||||
"""Perform image dithering by error diffusion method.
|
||||
|
||||
.. note:: Error diffusion is totally unoptimized and therefore very slow.
|
||||
It is included more as a reference implementation than as a useful
|
||||
method.
|
||||
|
||||
Reference:
|
||||
http://bisqwit.iki.fi/jutut/kuvat/ordered_dither/error_diffusion.txt
|
||||
|
||||
Quantization error of *current* pixel is added to the pixels
|
||||
on the right and below according to the formulas below.
|
||||
This works nicely for most static pictures, but causes
|
||||
an avalanche of jittering artifacts if used in animation.
|
||||
|
||||
Floyd-Steinberg:
|
||||
|
||||
* 7
|
||||
3 5 1 / 16
|
||||
|
||||
Jarvis-Judice-Ninke:
|
||||
|
||||
* 7 5
|
||||
3 5 7 5 3
|
||||
1 3 5 3 1 / 48
|
||||
|
||||
Stucki:
|
||||
|
||||
* 8 4
|
||||
2 4 8 4 2
|
||||
1 2 4 2 1 / 42
|
||||
|
||||
Burkes:
|
||||
|
||||
* 8 4
|
||||
2 4 8 4 2 / 32
|
||||
|
||||
|
||||
Sierra3:
|
||||
|
||||
* 5 3
|
||||
2 4 5 4 2
|
||||
2 3 2 / 32
|
||||
|
||||
Sierra2:
|
||||
|
||||
* 4 3
|
||||
1 2 3 2 1 / 16
|
||||
|
||||
Sierra-2-4A:
|
||||
|
||||
* 2
|
||||
1 1 / 4
|
||||
|
||||
Stevenson-Arce:
|
||||
|
||||
* . 32
|
||||
12 . 26 . 30 . 16
|
||||
. 12 . 26 . 12 .
|
||||
5 . 12 . 12 . 5 / 200
|
||||
|
||||
Atkinson:
|
||||
|
||||
* 1 1 / 8
|
||||
1 1 1
|
||||
1
|
||||
|
||||
:param :class:`PIL.Image` image: The image to apply error
|
||||
diffusion dithering to.
|
||||
:param :class:`~hitherdither.colour.Palette` palette: The palette to use.
|
||||
:param str method: The error diffusion map to use.
|
||||
:param int order: Metric parameter ``ord`` to send to
|
||||
:method:`numpy.linalg.norm`.
|
||||
:return: The error diffusion dithered PIL image of type
|
||||
"P" using the input palette.
|
||||
|
||||
"""
|
||||
ni = np.array(image, "float")
|
||||
|
||||
diff_map = _DIFFUSION_MAPS.get(method.lower())
|
||||
|
||||
for y in range(ni.shape[0]):
|
||||
for x in range(ni.shape[1]):
|
||||
old_pixel = ni[y, x]
|
||||
old_pixel[old_pixel < 0.0] = 0.0
|
||||
old_pixel[old_pixel > 255.0] = 255.0
|
||||
new_pixel = palette.pixel_closest_colour(old_pixel, order)
|
||||
quantization_error = old_pixel - new_pixel
|
||||
ni[y, x] = new_pixel
|
||||
for dx, dy, diffusion_coefficient in diff_map:
|
||||
xn, yn = x + dx, y + dy
|
||||
if (0 <= xn < ni.shape[1]) and (0 <= yn < ni.shape[0]):
|
||||
ni[yn, xn] += quantization_error * diffusion_coefficient
|
||||
return palette.create_PIL_png_from_rgb_array(np.array(ni, "uint8"))
|
|
@ -1,21 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
exceptions
|
||||
-----------
|
||||
|
||||
:copyright: 2017-05-10 by hbldh <henrik.blidh@nedomkull.com>
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import absolute_import
|
||||
|
||||
|
||||
class HitherDitherError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class PaletteCouldNotBeCreatedError(Exception):
|
||||
pass
|
|
@ -1,3 +0,0 @@
|
|||
from . import bayer
|
||||
from . import yliluoma
|
||||
from . import cluster
|
|
@ -1,88 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
bayer_dithering
|
||||
-----------
|
||||
|
||||
:copyright: 2016-09-09 by hbldh <henrik.blidh@nedomkull.com>
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def B(n, transposed=False):
|
||||
"""Get the Bayer matrix with side of length ``n``.
|
||||
|
||||
Will only work if ``n`` is a power of 2.
|
||||
|
||||
Reference: http://caca.zoy.org/study/part2.html
|
||||
|
||||
:param int n: Power of 2 side length of matrix.
|
||||
:return: The Bayer matrix.
|
||||
|
||||
"""
|
||||
return (1 + I(n, transposed)) / (1 + (n * n))
|
||||
|
||||
|
||||
def I(n, transposed=False):
|
||||
"""Get the index matrix with side of length ``n``.
|
||||
|
||||
Will only work if ``n`` is a power of 2.
|
||||
|
||||
Reference: http://caca.zoy.org/study/part2.html
|
||||
|
||||
:param int n: Power of 2 side length of matrix.
|
||||
:param bool transposed:
|
||||
:return: The index matrix.
|
||||
|
||||
"""
|
||||
if n == 2:
|
||||
if transposed:
|
||||
return np.array([[0, 3], [2, 1]], "int")
|
||||
else:
|
||||
return np.array([[0, 2], [3, 1]], "int")
|
||||
else:
|
||||
smaller_I = I(n >> 1, transposed)
|
||||
if transposed:
|
||||
return np.bmat(
|
||||
[
|
||||
[4 * smaller_I, 4 * smaller_I + 3],
|
||||
[4 * smaller_I + 2, 4 * smaller_I + 1],
|
||||
]
|
||||
)
|
||||
else:
|
||||
return np.bmat(
|
||||
[
|
||||
[4 * smaller_I, 4 * smaller_I + 2],
|
||||
[4 * smaller_I + 3, 4 * smaller_I + 1],
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def bayer_dithering(image, palette, thresholds, order=8):
|
||||
"""Render the image using the ordered Bayer matrix dithering pattern.
|
||||
|
||||
:param :class:`PIL.Image` image: The image to apply
|
||||
Bayer ordered dithering to.
|
||||
:param :class:`~hitherdither.colour.Palette` palette: The palette to use.
|
||||
:param thresholds: Thresholds to apply dithering at.
|
||||
:param int order: The size of the Bayer matrix.
|
||||
:return: The Bayer matrix dithered PIL image of type "P"
|
||||
using the input palette.
|
||||
|
||||
"""
|
||||
bayer_matrix = B(order)
|
||||
ni = np.array(image, "uint8")
|
||||
thresholds = np.array(thresholds, "uint8")
|
||||
xx, yy = np.meshgrid(range(ni.shape[1]), range(ni.shape[0]))
|
||||
xx %= order
|
||||
yy %= order
|
||||
factor_threshold_matrix = np.expand_dims(bayer_matrix[yy, xx], axis=2) * thresholds
|
||||
new_image = ni + factor_threshold_matrix
|
||||
return palette.create_PIL_png_from_rgb_array(new_image)
|
|
@ -1,67 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
bayer_dithering
|
||||
-----------
|
||||
|
||||
:copyright: 2016-09-09 by hbldh <henrik.blidh@nedomkull.com>
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
import numpy as np
|
||||
|
||||
_CLUSTER_DOT_MATRICES = {
|
||||
4: np.array([[12, 5, 6, 13], [4, 0, 1, 7], [11, 3, 2, 8], [15, 10, 9, 14]], "float")
|
||||
/ 16.0,
|
||||
8: np.array(
|
||||
[
|
||||
[24, 10, 12, 26, 35, 47, 49, 37],
|
||||
[8, 0, 2, 14, 45, 59, 61, 51],
|
||||
[22, 6, 4, 16, 43, 57, 63, 53],
|
||||
[30, 20, 18, 28, 33, 41, 55, 39],
|
||||
[34, 46, 48, 36, 25, 11, 13, 27],
|
||||
[44, 57, 60, 50, 9, 1, 3, 15],
|
||||
[42, 56, 62, 52, 23, 7, 5, 17],
|
||||
[32, 40, 54, 38, 31, 21, 19, 29],
|
||||
],
|
||||
"float",
|
||||
)
|
||||
/ 64.0,
|
||||
(5, 3): np.array([[9, 3, 0, 6, 12], [10, 4, 1, 7, 13], [11, 5, 2, 8, 14]], "float")
|
||||
/ 15.0,
|
||||
}
|
||||
|
||||
|
||||
def cluster_dot_dithering(image, palette, thresholds, order=4):
|
||||
"""Render the image using the ordered Bayer matrix dithering pattern.
|
||||
|
||||
Reference: http://caca.zoy.org/study/part2.html
|
||||
|
||||
:param :class:`PIL.Image` image: The image to apply the
|
||||
ordered dithering to.
|
||||
:param :class:`~hitherdither.colour.Palette` palette: The palette to use.
|
||||
:param thresholds: Thresholds to apply dithering at.
|
||||
:param int order: The size of the Bayer matrix.
|
||||
:return: The Bayer matrix dithered PIL image of type "P"
|
||||
using the input palette.
|
||||
|
||||
"""
|
||||
|
||||
cluster_dot_matrix = _CLUSTER_DOT_MATRICES.get(order)
|
||||
if cluster_dot_matrix is None:
|
||||
raise NotImplementedError("Only order 4 and 8 is implemented as of yet.")
|
||||
ni = np.array(image, "uint8")
|
||||
thresholds = np.array(thresholds, "uint8")
|
||||
xx, yy = np.meshgrid(range(ni.shape[1]), range(ni.shape[0]))
|
||||
xx %= order
|
||||
yy %= order
|
||||
factor_threshold_matrix = (
|
||||
np.expand_dims(cluster_dot_matrix[yy, xx], axis=2) * thresholds
|
||||
)
|
||||
new_image = ni + factor_threshold_matrix
|
||||
return palette.create_PIL_png_from_rgb_array(new_image)
|
|
@ -1 +0,0 @@
|
|||
from ._algorithm_one import yliluomas_1_ordered_dithering
|
|
@ -1,180 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
algorithm_one
|
||||
-----------
|
||||
|
||||
:copyright: 2016-09-12 by hbldh <henrik.blidh@nedomkull.com>
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ._utils import color_compare, CCIR_LUMINOSITY
|
||||
from ..bayer import I
|
||||
|
||||
|
||||
def _get_mixing_plan_matrix(palette, order=8):
|
||||
mixing_matrix = []
|
||||
colours = {}
|
||||
colour_component_distances = []
|
||||
|
||||
nn = order * order
|
||||
for i in range(len(palette)):
|
||||
for j in range(i, len(palette)):
|
||||
for ratio in range(0, nn):
|
||||
if i == j and ratio != 0:
|
||||
break
|
||||
# Determine the two component colors.
|
||||
c_mix = _colour_combine(palette, i, j, ratio / nn)
|
||||
hex_colour = palette.rgb2hex(*c_mix.tolist())
|
||||
colours[hex_colour] = (i, j, ratio / nn)
|
||||
mixing_matrix.append(c_mix)
|
||||
|
||||
c1 = np.array(palette[i], "int")
|
||||
c2 = np.array(palette[j], "int")
|
||||
cmpval = (
|
||||
color_compare(c1, c2)
|
||||
* 0.1
|
||||
* (np.abs((ratio / float(nn)) - 0.5) + 0.5)
|
||||
)
|
||||
colour_component_distances.append(cmpval)
|
||||
|
||||
mixing_matrix = np.array(mixing_matrix)
|
||||
colour_component_distances = np.array(colour_component_distances)
|
||||
|
||||
for c in mixing_matrix:
|
||||
assert palette.rgb2hex(*c.tolist()) in colours
|
||||
|
||||
return mixing_matrix, colours, colour_component_distances
|
||||
|
||||
|
||||
def _colour_combine(palette, i, j, ratio):
|
||||
c1, c2 = np.array(palette[i], "int"), np.array(palette[j], "int")
|
||||
return np.array(c1 + ratio * (c2 - c1), "uint8")
|
||||
|
||||
|
||||
def _improved_mixing_error_fcn(
|
||||
colour, mixing_matrix, colour_component_distances, luma_mat=None
|
||||
):
|
||||
"""Compares two colours using the Psychovisual model.
|
||||
|
||||
The simplest way to adjust the psychovisual model is to
|
||||
add some code that considers the difference between the
|
||||
two pixel values that are being mixed in the dithering
|
||||
process, and penalizes combinations that differ too much.
|
||||
|
||||
Wikipedia has an entire article about the topic of comparing
|
||||
two color values. Most of the improved color comparison
|
||||
functions are based on the CIE colorspace, but simple
|
||||
improvements can be done in the RGB space too. Such a simple
|
||||
improvement is shown below. We might call this RGBL, for
|
||||
luminance-weighted RGB.
|
||||
|
||||
:param :class:`numpy.ndarray` colour: The colour to estimate error to.
|
||||
:param :class:`numpy.ndarray` mixing_matrix: The rgb
|
||||
values of mixed colours.
|
||||
:param :class:`numpy.ndarray` colour_component_distances: The colour
|
||||
distance of the mixed colours.
|
||||
:return: :class:`numpy.ndarray`
|
||||
|
||||
"""
|
||||
colour = np.array(colour, "int")
|
||||
if luma_mat is None:
|
||||
luma_mat = mixing_matrix.dot(CCIR_LUMINOSITY / 1000.0 / 255.0)
|
||||
luma_colour = colour.dot(CCIR_LUMINOSITY) / (255.0 * 1000.0)
|
||||
luma_diff_squared = (luma_mat - luma_colour) ** 2
|
||||
diff_colour_squared = ((colour - mixing_matrix) / 255.0) ** 2
|
||||
cmpvals = diff_colour_squared.dot(CCIR_LUMINOSITY) / 1000.0
|
||||
cmpvals *= 0.75
|
||||
cmpvals += luma_diff_squared
|
||||
cmpvals += colour_component_distances
|
||||
return cmpvals
|
||||
|
||||
|
||||
def yliluomas_1_ordered_dithering(image, palette, order=8):
|
||||
"""A dithering method that weighs in color combinations of palette.
|
||||
|
||||
N.B. tri-tone dithering is not implemented.
|
||||
|
||||
:param :class:`PIL.Image` image: The image to apply
|
||||
Bayer ordered dithering to.
|
||||
:param :class:`~hitherdither.colour.Palette` palette: The palette to use.
|
||||
:param int order: The Bayer matrix size to use.
|
||||
:return: The dithered PIL image of type "P" using the input palette.
|
||||
|
||||
"""
|
||||
bayer_matrix = I(order, transposed=True) / 64.0
|
||||
ni = np.array(image, "uint8")
|
||||
xx, yy = np.meshgrid(range(ni.shape[1]), range(ni.shape[0]))
|
||||
factor_matrix = bayer_matrix[yy % order, xx % order]
|
||||
|
||||
# Prepare all precalculated mixed colours and their respective
|
||||
mixing_matrix, colour_map, colour_component_distances = _get_mixing_plan_matrix(
|
||||
palette
|
||||
)
|
||||
mixing_matrix = np.array(mixing_matrix, "int")
|
||||
luma_mat = mixing_matrix.dot(CCIR_LUMINOSITY / 1000.0 / 255.0)
|
||||
|
||||
color_matrix = np.zeros(ni.shape[:2], dtype="uint8")
|
||||
for x, y in zip(np.nditer(xx), np.nditer(yy)):
|
||||
|
||||
min_index = np.argmin(
|
||||
_improved_mixing_error_fcn(
|
||||
ni[y, x, :], mixing_matrix, colour_component_distances, luma_mat
|
||||
)
|
||||
)
|
||||
closest_mix_colour = mixing_matrix[min_index, :].tolist()
|
||||
closest_mix_hexcolour = palette.rgb2hex(*closest_mix_colour)
|
||||
plan = colour_map.get(closest_mix_hexcolour)
|
||||
color_matrix[y, x] = plan[1] if (factor_matrix[y, x] < plan[-1]) else plan[0]
|
||||
|
||||
return palette.create_PIL_png_from_closest_colour(color_matrix)
|
||||
|
||||
|
||||
def _evaluate_mixing_error(
|
||||
desired_colour,
|
||||
mixed_colour,
|
||||
component_colour_1,
|
||||
component_colour_2,
|
||||
ratio,
|
||||
component_colour_compare_value=None,
|
||||
):
|
||||
"""Compare colours and weigh in component difference.
|
||||
|
||||
double EvaluateMixingError(int r,int g,int b,
|
||||
int r0,int g0,int b0,
|
||||
int r1,int g1,int b1,
|
||||
int r2,int g2,int b2,
|
||||
double ratio)
|
||||
{
|
||||
return ColorCompare(r,g,b, r0,g0,b0)
|
||||
+ ColorCompare(r1,g1,b1, r2,g2,b2) * 0.1
|
||||
* (fabs(ratio-0.5)+0.5);
|
||||
}
|
||||
|
||||
|
||||
:param desired_colour:
|
||||
:param mixed_colour:
|
||||
:param component_colour_1:
|
||||
:param component_colour_2:
|
||||
:param ratio:
|
||||
:param component_colour_compare_value:
|
||||
:return:
|
||||
|
||||
"""
|
||||
if component_colour_compare_value is None:
|
||||
return color_compare(desired_colour, mixed_colour) + (
|
||||
color_compare(component_colour_1, component_colour_2)
|
||||
* 0.1
|
||||
* (np.abs(ratio - 0.5) + 0.5)
|
||||
)
|
||||