0

I coded FaceCropped using cv2 and it works!
I want to put img through facecropped method in ProcessedImageField model.
Is there anyway? What I want is to automatically cut the face and put it in the database when uploading pictures into student model.

method facecropped

import numpy as np
import cv2
import os
import glob

def FaceCropped(full_path, extra='face', show=False):
    face_cascade = cv2.CascadeClassifier('C:../haarcascade_frontalface_default.xml')
    full_path = full_path
    path,file = os.path.split(full_path)


    ff = np.fromfile(full_path, np.uint8)
    img = cv2.imdecode(ff, cv2.IMREAD_UNCHANGED)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, 1.3,5)

    for (x,y,w,h) in faces:
        cropped = img[y - int(h/4):y + h + int(h/4), x - int(w/4):x + w + int(w/4)]
        result, encoded_img = cv2.imencode(full_path, cropped)
        if result:
            with open(path + '/' + extra + file, mode='w+b') as f:
                encoded_img.tofile(f)
    if show:
        cv2.imshow('Image view', cropped)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

my model.py

class Student(models.Model):
    picture = ProcessedImageField(
        verbose_name = 'picture',
        upload_to = 'students/%Y/',
        processors=[ResizeToFill(300,300)],
        options={'quality':80},
        format='JPEG',
        null=True,
        blank=True,
        default='students/no-img.jpg',
    )
    name = models.CharField(max_length=255)

my views.py

class StudentAdd(FormView):
    model = Student
    template_name = 'student/list_add.html'
    context_object_name = 'student'
    form_class = AddStudent

    def post(self, request):
        form = self.form_class(request.POST, request.FILES)

        if form.is_valid():
            student = form.save(commit=False)
            student.created_by = request.user

            student.save()
            messages.info(request, student.name + ' addddd', extra_tags='info')
            if "save_add" in self.request.POST:
                return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
            return redirect('student_detail', pk=student.pk, school_year=student.school_year)

        return FormView.post(self, request)
Community
  • 1
  • 1
chea
  • 101
  • 1
  • 11

2 Answers2

1

There several ways to achieve this.

Override Model Save Method

One way to save crop Image into database is that you need to override the save method.
Have a look at this document.

You can do whatever you want to process on model data before you save data into database. You’re free to override these methods (and any other model method) to alter behavior.

The code below is from django document.

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        do_something()
        super().save(*args, **kwargs)  # Call the "real" save() method.
        do_something_else()

Inherit Field class and override clean method

I assumed you are using ProcessedImageField from imagekit

from imagekit.models import ProcessedImageField

You can inherit ProcessedImageField and override the clean method of Field classes.

Clean method ensure that all data in field is cleaned. Image processing before saving can be in clean method.

The code below is from django source code.

def clean(self, value):
    """
    Validate the given value and return its "cleaned" value as an
    appropriate Python object. Raise ValidationError for any errors.
    """
    value = self.to_python(value)
    self.validate(value)
    self.run_validators(value)
    return value

Override Form clean method

This question explain how to override form clean method.
Clean method will run before save method.
This is another common way to change the data before saving.

Misster Hao
  • 92
  • 1
  • 1
  • 10
  • @chea what does "update model" means ? Changing model schema? or save again – Misster Hao Jan 29 '21 at 07:44
  • technically save again, i mean update a model that exists already – chea Jan 29 '21 at 07:59
  • https://stackoverflow.com/questions/65950681/django-cut-and-put-only-the-face-in-the-picture-field-using-opencv could you check it out ? cheers – chea Jan 29 '21 at 08:00
0

I solved it !
Cheers Misster Hao!
Can we optimize the code more?

in model.py

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        FaceCropped(self.picture.path)
def FaceCropped(full_path):
    face_cascade = cv2.CascadeClassifier('D:/Dropbox/atom/django/Manage/Project/student/haarcascade_frontalface_default.xml')
    full_path = full_path
    path,file = os.path.split(full_path)


    ff = np.fromfile(full_path, np.uint8)
    img = cv2.imdecode(ff, cv2.IMREAD_UNCHANGED)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, 1.3,5)

    for (x,y,w,h) in faces:
        cropped = img[y - int(h/4):y + h + int(h/4), x - int(w/4):x + w + int(w/4)]
        result, encoded_img = cv2.imencode(full_path, cropped)
        if result:
            with open(full_path, mode='w+b') as f:
                encoded_img.tofile(f)
chea
  • 101
  • 1
  • 11
  • and when i update model , def save works always, how can i don't let it work when i update it? – chea Jan 29 '21 at 07:27
  • and i want to ProcessedImageField from imagekit works first and FaceCropped works – chea Jan 29 '21 at 07:35