I am a lifelong learner, creator, explorer, and tinkerer. This is a collection of my experiences.
I once read somewhere that burnout is the continued application of large amounts of energy on projects that ultimately end in failure. In the last year or so I've started and failed many projects. To name a few:
(I even made a repository just for them!)
All of these projects were large energy consumers. I kept starting projects, getting tired of the energy demanded, and giving up.
I had a graveyard full of half completed projects. My love of side projects was inevitably going to lead me to burnout if I maintained this trend. So I decided to do something about it. No more overly complex projects with no end in sight. Instead, I decided to focus on little side projects that would be fun to build and that others might enjoy as well.
I came across this lovely CSS Cross Stitch of the Pokemon Cyndaquil by Olivia Ng. (Screenshot of the resulting image below)
To make sharing my fun projects with the world - I decided it was time for new hosting. After talking with a devops engineering coworker I decided to go with Google Cloud Platform. I knew almost nothing about it but figured it was time to learn. This project would be my first larger endeavor.
I banged my head against the wall, a lot. I tried to keep track of all the resources that I used but in the end gave up. Here is what just one day of my browsing history for "Google Cloud Platform" looked like:
The above image goes to show that even with several years of experience, I'm still Googling lots of things.
I am super grateful to the developer community and was able to keep track of some of the more influential/helpful/inspirational pages I found:
Everything related to GCP in this project was brand new to me. I used to develop with Flask but had to go read some refersher content.
There are three intersting journeys here, denoted by the three circles.
The Flask and Firebase article I mentioned above helped with this first part.
Cloud Build and Cloud Run are tools that collectively took the code off my machine, created a Docker image with it, and sent it to GCP so that users could access the site.
The end user gets a webpage where they can enter a photo. That photo goes off to the API which is powered by Flask. Flask sends the photo to my stitch_image function and returns the resulting CSS to the user.
I have been rather fearful of racking up a large bill with this project. I setup a little trigger here that when my bill passes a certain threshold, I get a message to phone telling me so.
I've scattered comments throughout the code below.
# 1. Read image from file upload
npimg = np.frombuffer(filestr, np.uint8)
img = cv2.imdecode(npimg, cv2.IMREAD_UNCHANGED)
# 2. Resize image and handle various file types
img = imutils.resize(img, width=1000)
img = imutils.resize(img, height=1000)
input_width, input_height, *_ = img.shape
if len(img.shape) > 2 and img.shape[2] == 4:
img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
sample_size = round(input_width / horizontal_samples_user_input)
vertical_samples = round(input_height / sample_size)
# 3. Create the output HTML string that will hold all of the resulting CSS image
output_html = ''
output_html += '\t\t<div id="wrapper"><div id="image">\n'
# 4. Iterate over the image, left to right, top to bottom, in small sections.
for i in range(0, horizontal_samples_user_input):
output_html += '\t\t\t<div class="row">\n'
for j in range(0, vertical_samples):
i_start = i*sample_size
i_end = i*sample_size+sample_size
j_start = j*sample_size
j_end = j*sample_size+sample_size
# 5. See below for kmeans_image()
b,g,r = kmeans_image(img[i_start:i_end, j_start:j_end])
output_html += f'\t\t\t\t<div style="background-color: rgb({r},{g},{b});" class="cell">'
output_html += '</div>\n'
output_html += '\t\t\t</div>\n'
# 7. The HTML string is returned back to Flask to send to the user.
output_html += '\t\t</div></div>\n'
return output_html
# 6. If you just took the average RGB values in a given section of an image, you
# would probably just end up with gray or brown. K Means clustering is a way to
# look at all the colors in three dimensional space - red one axis, green the next
# blue the last and trying to group similar RGB values together and then take their
# averages locally. This results in much more interesting colors. Read the Stack Overflow
# link above to learn more.
def kmeans_image(img):
pixels = np.float32(img.reshape(-1, 3))
n_colors = 3
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 200, .1)
flags = cv2.KMEANS_RANDOM_CENTERS
_, labels, palette = cv2.kmeans(pixels, n_colors, None, criteria, 10, flags)
_, counts = np.unique(labels, return_counts=True)
return np.uint8(palette[-1])
I've taken a few of my own photographs and run them through.