-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.py
195 lines (166 loc) · 6.79 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# General imports
import sys
import os
import logging
from datetime import datetime
import math
import random
from PIL import Image, ImageDraw
import requests # For retrieving artwork data and image
# Set up logging
logging.basicConfig(level=logging.DEBUG)
# Prepare directories so they can be reached from anywhere
appDir = os.path.dirname(os.path.realpath(__file__))
assetsDir = os.path.join(appDir, "assets")
# Get required items from other root-level directories
parentDir = os.path.dirname(appDir)
libDir = os.path.join(parentDir, "lib")
if os.path.exists(libDir):
sys.path.append(libDir)
# Change the below import to match your display's driver
from waveshare_epd import epd7in5_V2 as display
# Adjust your optical offsets from one place
# import layout
# See Pi Frame for usage:
# https://github.com/dnywh/pi-frame
# Settings
headers = {"AIC-User-Agent": "Art Press ([email protected])"} # As a courtesy
pageItemLimit = 10 # 100 or less per page
# Design options
# Shared optical sizing and offsets with Pi Frame
# maskSize = layout.size
# offsetX = layout.offsetX
# offsetY = layout.offsetY
# Manual optical sizing and offsets
maskWidth = 740
maskHeight = 450
# maskSize = 420
offsetX = 0
offsetY = 12
imageQuality = "bitonal" # Options are "default", "gray", "bitonal"
preferCrop = True # Crop to center of original image if true
imageWidth = 843 # Preferred width as per ARTIC API documentation
# Other
exportImages = False # Save both the input and output image in an exports folder
criteria = {
"query": {
"bool": {
"must": [
{"term": {"is_public_domain": True}},
{"match": {"classification_title": "woodcut"}},
# {"match": {"term_titles": "woodcut"}},
# {"match": {"classification_titles": "etching"}},
# {"match": {"subject_titles": "geometric"}},
# {"range": {"color.h": {"lt": 2}}},
],
"should": [
{"term": {"is_boosted": True}},
],
"must_not": [
{"match": {"medium_display": "Ceramic and pigment"}},
{"match": {"medium_display": "Plant fibers"}},
{"match": {"term_titles": "metalwork"}},
],
}
}
}
try:
# Local time
timeStampNice = datetime.today().strftime("%Y-%m-%d %H:%M:%S")
logging.info(f"Kicking off at {timeStampNice}")
# Post a bare minimum query to find out the amount of pages the criteria returns
url = f"https://api.artic.edu/api/v1/artworks/search?limit=0"
r = requests.post(url, headers=headers, json=criteria)
art = r.json()
# Get results
resultSize = art["pagination"]["total"]
pages = int(math.ceil(resultSize / 10)) # Fixed to 10 items per page?
logging.info(f"There are {resultSize} possible choices for your criteria")
# Select a random page
randomPage = random.randint(0, pages - 1)
logging.info(f"Randomly selected page {randomPage + 1} of {pages}")
# Post the full query into this page
# Fields to include in data
fields = "api_link,image_id,title,artist_id,artist_title,medium_display,thumbnail" # Make sure to include any fields that are queried later on
url = f"https://api.artic.edu/api/v1/artworks/search?limit={pageItemLimit}&page={randomPage}&fields={fields}"
r = requests.post(url, headers=headers, json=criteria)
art = r.json()
# Select a random item
randomArt = random.randint(0, pageItemLimit - 1)
logging.info(f"Randomly selected item {randomArt + 1} of {pageItemLimit}")
# Parse random item
apiLink = art["data"][randomArt]["api_link"]
imageId = art["data"][randomArt]["image_id"]
imageTitle = art["data"][randomArt]["title"]
imageArtist = art["data"][randomArt]["artist_title"]
# Crop artwork
if preferCrop == True:
# Calculate center crop of image
imageWidth = art["data"][randomArt]["thumbnail"]["width"]
imageHeight = art["data"][randomArt]["thumbnail"]["height"]
cropStartX = int((imageWidth - maskWidth) / 2)
cropStartY = int((imageHeight - maskHeight) / 2)
# Pass this crop region into the image parameters
cropRegion = f"{cropStartX},{cropStartY},{maskWidth},{maskHeight}"
imageParams = f"/{cropRegion}/{maskWidth},/0/{imageQuality}.jpg"
else:
# Just get the square version of the image, resized to canvas size
imageParams = f"/square/{maskWidth},/0/{imageQuality}.jpg"
artworkUrl = f"https://www.artic.edu/iiif/2/{imageId}{imageParams}"
canonicalArtworkUrl = (
f"https://www.artic.edu/iiif/2/{imageId}/full/843,/0/default.jpg"
)
logging.info(f"Artwork URL: {artworkUrl}")
# Download a temporary copy of the map tile from the API to render to screen
r = requests.get(artworkUrl, headers=headers)
# Store it locally
artworkImagePath = os.path.join(appDir, "artwork.jpg")
with open(artworkImagePath, "wb") as f:
f.write(r.content)
# Prepare versions of image
artwork = Image.open(artworkImagePath)
# Throw away original artwork image since new versions have been made
os.remove(artworkImagePath)
# Log information, including a URL for the original artwork
output = f"Printed at:\t{timeStampNice}\nTitle:\t\t{imageTitle}\nArtist:\t\t{imageArtist}\nAPI URL:\t{apiLink}\nImage URL:\t{canonicalArtworkUrl}"
# ...to console
logging.info(f"\n{output}")
# Save out
if exportImages == True:
# Prepare directory for saving image(s)
exportsDir = os.path.join(appDir, "exports")
timeStampSlugToMin = datetime.today().strftime("%Y-%m-%d-%H-%M")
imageDir = os.path.join(exportsDir, timeStampSlugToMin)
if not os.path.exists(exportsDir):
os.makedirs(exportsDir)
if not os.path.exists(imageDir):
os.mkdir(imageDir)
# Save image in its directory
artwork.save(os.path.join(imageDir, f"{timeStampSlugToMin}.jpg"))
# Also save text output
with open(os.path.join(imageDir, f"{timeStampSlugToMin}.txt"), "w") as f:
f.write(output)
# Start rendering
epd = display.EPD()
epd.init()
epd.Clear()
canvas = Image.new("1", (epd.width, epd.height), "white")
draw = ImageDraw.Draw(canvas)
# Calculate top-left starting position
startX = offsetX + int((epd.width - maskWidth) / 2)
startY = offsetY + int((epd.height - maskHeight) / 2)
canvas.paste(artwork, (startX, startY))
# Render all of the above to the display
epd.display(epd.getbuffer(canvas))
# Put display on pause, keeping what's on screen
epd.sleep()
logging.info(f"Finishing printing. Enjoy.")
# Exit application
exit()
except IOError as e:
logging.info(e)
# Exit plan
except KeyboardInterrupt:
logging.info("Exited.")
display.epdconfig.module_exit()
exit()