Cropping an Image with Javascript and Python Next Lecture
updated Oct. 14, 2020

  1. cropperjs library
  2. python opencv

Code changes:

  1. /static/cropperjs/
  2. account/views.py
  3. account/urls.py
  4. base.html
  5. account/edit_account.html


Download cropperjs library.

  • Timestamp: 1:40

  • Find on GitHub: cropperjs library

  • On GitHub, click on "Code" (Green button) -> Download ZIP: to download the ZIP file.

  • Extract the ZIP folder cropperjs-master.zip we downloaded.



Create a account/templates/account folder.

  • Timestamp: 2:20

  • Find on GitHub: static/cropperjs

  • Copy all the files/folders in cropperjs-master folder except the .github folder and paste them into static/cropperjs



Collect Static.

Collects the static files into STATIC_ROOT. Our STATIC_ROOT is os.path.join(BASE_DIR, 'static_cdn')

  • Timestamp: 2:31

  • Inside (venv) D:\DjangoProjects\ChatServerPlayground\venv\src

    • Run: python manage.py collectstatic

    • Optional: If it asks for confirmation, type yes and hit Enter.

    • Output: XXX static files copied to D:\DjangoProjects\ChatServerPlayground\venv\src\static_cdn. Open the static_cdn folder locally and you will see files/folders copied there as shown in the video.



Install opencv-python.

Collects the static files into STATIC_ROOT. Our STATIC_ROOT is os.path.join(BASE_DIR, 'static_cdn')

  • Timestamp: 3:50

  • Inside (venv) D:\DjangoProjects\ChatServerPlayground\venv\src

    • Run: pip install opencv-python

    • Run: pip freeze > requirements.txt to record an environment's current package list into requirements.txt.



Update account/views.py file.


# TODO: ADD THESE IMPORTS TO THE TOP OF THE FILE.

from django.core.files.storage import default_storage
from django.core.files.storage import FileSystemStorage
import os
import cv2
import json
import base64
import requests
from django.core import files


ADD THESE LINE IMMEDIATELY BELOW THE IMPORTS.

TEMP_PROFILE_IMAGE_NAME = "temp_profile_image.png"


# TODO: ADD THESE METHODS.

def save_temp_profile_image_from_base64String(imageString, user):
	INCORRECT_PADDING_EXCEPTION = "Incorrect padding"
	try:
		if not os.path.exists(settings.TEMP):
			os.mkdir(settings.TEMP)
		if not os.path.exists(settings.TEMP + "/" + str(user.pk)):
			os.mkdir(settings.TEMP + "/" + str(user.pk))
		url = os.path.join(settings.TEMP + "/" + str(user.pk),TEMP_PROFILE_IMAGE_NAME)
		storage = FileSystemStorage(location=url)
		image = base64.b64decode(imageString)
		with storage.open('', 'wb+') as destination:
			destination.write(image)
			destination.close()
		return url
	except Exception as e:
		print("exception: " + str(e))
		# workaround for an issue I found
		if str(e) == INCORRECT_PADDING_EXCEPTION:
			imageString += "=" * ((4 - len(imageString) % 4) % 4)
			return save_temp_profile_image_from_base64String(imageString, user)
	return None


def crop_image(request, *args, **kwargs):
	payload = {}
	user = request.user
	if request.POST and user.is_authenticated:
		try:
			imageString = request.POST.get("image")
			url = save_temp_profile_image_from_base64String(imageString, user)
			img = cv2.imread(url)

			cropX = int(float(str(request.POST.get("cropX"))))
			cropY = int(float(str(request.POST.get("cropY"))))
			cropWidth = int(float(str(request.POST.get("cropWidth"))))
			cropHeight = int(float(str(request.POST.get("cropHeight"))))
			if cropX < 0:
				cropX = 0
			if cropY < 0: # There is a bug with cropperjs. y can be negative.
				cropY = 0
			crop_img = img[cropY:cropY+cropHeight, cropX:cropX+cropWidth]

			cv2.imwrite(url, crop_img)

			# delete the old image
			user.profile_image.delete()

			# Save the cropped image to user model
			user.profile_image.save("profile_image.png", files.File(open(url, 'rb')))
			user.save()

			payload['result'] = "success"
			payload['cropped_profile_image'] = user.profile_image.url

			# delete temp file
			os.remove(url)

		except Exception as e:
			print("exception: " + str(e))
			payload['result'] = "error"
			payload['exception'] = str(e)
	return HttpResponse(json.dumps(payload), content_type="application/json")



Update account/urls.py file.


from account.views import (
	account_view,
	edit_account_view,
	crop_image, # TODO: ADD THiS LINE.
)

app_name = 'account'

urlpatterns = [
	path('<user_id>/', account_view, name="view"),
	path('<user_id>/edit/', edit_account_view, name="edit"),
	path('<user_id>/edit/cropImage/', crop_image, name="crop_image"), # TODO: ADD THiS LINE.
] 



Update templates/base.html file.


#<!-- TODO: ADD THIS LINE. -->

<!-- Cropperjs -->
<link rel="stylesheet" href="{% static 'cropperjs/dist/cropper.min.css' %}">


// TODO: ADD THESE SCRIPTS.

<script type="module" src="{% static 'cropperjs/dist/cropper.min.js' %}"></script>

<script type="text/javascript">
	function displayLoadingSpinner(isDisplayed){
		var spinner = document.getElementById("id_loading_spinner")
		if(isDisplayed){
			spinner.style.display = "block"
		}
		else{
			spinner.style.display = "none"
		}
	}
</script>


<!-- TODO: ADD THIS CLASS. -->

<div class="container" id="id_loading_spinner" style="display: none">
	<div class="d-flex flex-row mx-auto flex-grow-1 justify-content-center">
		<div class="spinner-border text-primary" role="status">
			<span class="sr-only">Loading...</span>
		</div>
	</div>
</div>



Update account/templates/account/edit_account.html file.





Author


Mitch Tabian

codingwithmitch.com

Software Dev


Lectures
  • Course Demo
  • Part 1: Project Setup
  • Project Setup
    FREE
  • Templates Setup
    FREE
  • Bootstrap and Static Files Setup
    FREE
  • Header and Footer Bootstrap Style Guide
    FREE
  • Part 2 - User Management
  • Custom User Model
    FREE
  • Register New Users
    FREE
  • Login and Logout
    FREE
  • Password Reset and Password Change
    FREE
  • User Account Screen
    FREE
  • User Search
    FREE
  • Editing User Account Properties
    FREE
  • Uploading an Image
    FREE
  • Cropping an Image with Javascript and Python
    FREE
  • Part 3: Friend System
  • Friend System
    FREE
  • Account Screen with Friend System
    FREE
  • Sending a Friend Request
    FREE
  • Querying Friend Requests
    FREE
  • Accepting a Friend Request
    FREE
  • Removing a Friend
    FREE
  • Decline a Friend Request
    FREE
  • Cancel a Friend Request
    FREE
  • Querying Friends
    FREE
  • Part 4: Public Chat
  • Public Chat App
    FREE
  • Installing Django Channels 2
    FREE
  • Public Chat Consumer
  • Sending a Payload to the Consumer
  • Formatting Chat Messages
  • Handling Consumer Errors
  • Django Humanize for Timestamp Formatting
  • Channel Layers for Broadcasting Messages
  • Saving Chat Messages to the Database
  • Querying Chatroom Messages
  • insertBefore and appendChild
  • Progress Indicator for Pagination
  • Display Connected Users
  • Public Chat Cleanup
  • Asynchronously Loading Images with Javascript
  • Security Risk and Markdown
  • Part 5: Private Chat
  • Private Chat App
  • Private Chatroom View
  • Creating Private Chatrooms
  • Post Save Receivers
  • Display Friends in Room
  • Private Chat Consumer
  • Private Chat WebSocket
  • Dynamically Connected a WebSocket
  • Get User Info Dynamically
  • Error Handling in Private Chat
  • Sending Private Chat Messages (1/2)
  • Sending Private Chat Messages (2/2)
  • Joining and Leaving a Chatroom
  • Querying Private Chatroom Messages
  • Private Chat Messages Payload to UI
  • Private Chat Pagination
  • Loading Spinner Private Chat
  • Highlighting the Selected Chatroom
  • Starting a Private Chat
  • Part 6: Notifications
  • Django Generic Relations (Notification Model)
  • Notification Admin
  • Notifications for Friend Actions
  • Notification Consumer
  • "General" Notification Payloads
  • Fetch FriendRequest & FriendList Notifications
  • Display FriendRequest & FriendList Notifications
  • Accepting a Friend Request from Notification
  • Declining a Friend Request from Notification
  • Pagination for General Notifications
  • Updating Notifications in Real-time
  • New General Notifications in Real-time
  • Unread Notifications Count
  • Private Chat Notification System
  • Tracking if Users are Connected to a Chat
  • Fetching Chatroom Message Notifications
  • Handling Chatroom Notification Payloads
  • Fetching New Chat Notifications in Real-time
  • Pagination for Chat Notifications
  • Displaying Count for Chat Notifications
  • Part 7: Push to Production
  • Push to Production



Comments


Lectures
  • Course Demo
  • Part 1: Project Setup
  • Project Setup
    FREE
  • Templates Setup
    FREE
  • Bootstrap and Static Files Setup
    FREE
  • Header and Footer Bootstrap Style Guide
    FREE
  • Part 2 - User Management
  • Custom User Model
    FREE
  • Register New Users
    FREE
  • Login and Logout
    FREE
  • Password Reset and Password Change
    FREE
  • User Account Screen
    FREE
  • User Search
    FREE
  • Editing User Account Properties
    FREE
  • Uploading an Image
    FREE
  • Cropping an Image with Javascript and Python
    FREE
  • Part 3: Friend System
  • Friend System
    FREE
  • Account Screen with Friend System
    FREE
  • Sending a Friend Request
    FREE
  • Querying Friend Requests
    FREE
  • Accepting a Friend Request
    FREE
  • Removing a Friend
    FREE
  • Decline a Friend Request
    FREE
  • Cancel a Friend Request
    FREE
  • Querying Friends
    FREE
  • Part 4: Public Chat
  • Public Chat App
    FREE
  • Installing Django Channels 2
    FREE
  • Public Chat Consumer
  • Sending a Payload to the Consumer
  • Formatting Chat Messages
  • Handling Consumer Errors
  • Django Humanize for Timestamp Formatting
  • Channel Layers for Broadcasting Messages
  • Saving Chat Messages to the Database
  • Querying Chatroom Messages
  • insertBefore and appendChild
  • Progress Indicator for Pagination
  • Display Connected Users
  • Public Chat Cleanup
  • Asynchronously Loading Images with Javascript
  • Security Risk and Markdown
  • Part 5: Private Chat
  • Private Chat App
  • Private Chatroom View
  • Creating Private Chatrooms
  • Post Save Receivers
  • Display Friends in Room
  • Private Chat Consumer
  • Private Chat WebSocket
  • Dynamically Connected a WebSocket
  • Get User Info Dynamically
  • Error Handling in Private Chat
  • Sending Private Chat Messages (1/2)
  • Sending Private Chat Messages (2/2)
  • Joining and Leaving a Chatroom
  • Querying Private Chatroom Messages
  • Private Chat Messages Payload to UI
  • Private Chat Pagination
  • Loading Spinner Private Chat
  • Highlighting the Selected Chatroom
  • Starting a Private Chat
  • Part 6: Notifications
  • Django Generic Relations (Notification Model)
  • Notification Admin
  • Notifications for Friend Actions
  • Notification Consumer
  • "General" Notification Payloads
  • Fetch FriendRequest & FriendList Notifications
  • Display FriendRequest & FriendList Notifications
  • Accepting a Friend Request from Notification
  • Declining a Friend Request from Notification
  • Pagination for General Notifications
  • Updating Notifications in Real-time
  • New General Notifications in Real-time
  • Unread Notifications Count
  • Private Chat Notification System
  • Tracking if Users are Connected to a Chat
  • Fetching Chatroom Message Notifications
  • Handling Chatroom Notification Payloads
  • Fetching New Chat Notifications in Real-time
  • Pagination for Chat Notifications
  • Displaying Count for Chat Notifications
  • Part 7: Push to Production
  • Push to Production