Crop d'une photo

Hello la communauté,

 

Voila je viens vers vous, car mes recherches ont été jusqu'à  présent infructueuses sur le sujet.

 

Dans le cadre d'un projet en Swift 3 j'ai besoin de faire un crop photo (comme sur l'image ci dessous) après que l'utilisateur ai pris une photo avec l'appareil ou en ai sélectionné une dans sa librairie (via l'utilisation de UIImagePickerController)

 

En cherchant un peu j'ai pu trouver le Pod AAPhotoCircleCrop qui ne fait qu'en partie ce que je souhaite (en effet ce dernier ne prends pas la totalité de l'écran, j'ai cru comprendre qu'il fallait modifier la courbe de Béziers).


 


J'avoue bloquer et c'est pour cela que je viens solliciter votre aide.


 


Bonne journée à  toutes et à  tous :)


Mots clés:
«1

Réponses

  • Ben77650Ben77650 Membre
    mars 2017 modifié #3

    Merci Insou je regarde ça :)


     


    PS: J'étais tombé dessus mais c'est un crop circulaire dont j'ai besoin, je sais pas si ça le fera ces projets la...


  • Je l'ai pas précisé mais le "crop" doit être un "filtre" par dessus la photo et on ne pourra pas bouger ce "filtre", il faudrait bouger et / ou zoomer / dézoomer la photo donc le 2e me parait pas correspondre à  ce que je souhaite ;)


  • InsouInsou Membre

    et avec ça ?


    http://stackoverflow.com/questions/29046571/cut-a-uiimage-into-a-circle-swiftios


     


    // Edit


    Pour l'histoire du filtre par dessus, aucune idée comment faire..


    Peut-être que c'est juste un filtre visuel, pour faire un aperçu du rendu et que tu peux faire le job avec ce code là  :p


  • Je regarde ça.


     


    En fait l'idée c'est d'avoir ce filtre par dessus, et une fois le crop validé je doit garder que la photo qui se trouve dans le cercle dans le filtre (visiblement ça a déjà  été fait par un collègue en Obj-C mais "ça serait chiant de faire un header-bridge juste pour ça")


  • Pas sur que ça m'aide j'ai déjà  arrondi mon image actuelle en faisant ça

     



    profilePicImageView.image = flippedImage
    profilePicImageView.contentMode = .scaleAspectFill
    profilePicImageView.layer.cornerRadius = profilePicImageView.frame.size.width / 2
    profilePicImageView.clipsToBounds = true
    profilePicImageView.layer.borderWidth = 1
    profilePicImageView.layer.borderColor = UIColor.white.cgColor

  • C'est facile à  faire avec un contexte graphique. Quand tu dessines une image dans un contexte graphique, elle écrase le contenu précédent en l'écrasant. Mais on peut aussi utiliser un opérateur graphique pour "mélanger" les pixels de l'image avec le contenu précédent. Il y a de nombreux opérateurs :


     


    https://developer.apple.com/reference/coregraphics/cgblendmode


     


    L'opérateur .sourceAtop est particulièrement intéressant, parce qu'il mélange les couleurs en se basant sur les pixels de couleurs transparente. Cela permet de "filtrer" les pixels de l'image selon n'importe quelle forme.


    a



    func cropEllipse(image:UIImage, rect:CGRect) -> UIImage? {
    UIGraphicsBeginImageContext(image.size)
    let context = UIGraphicsGetCurrentContext()

    // Dessin d'un masque circulaire
    // de n'importe quelle couleur
    // (seul le canal alpha est utilisé dans l'opération)
    context?.setFillColor(UIColor.white.cgColor)
    context?.fillEllipse(in: rect)

    // Projection de l'image sur le masque
    // avec l'opérateur graphique .sourceAtop
    image.draw(at: CGPoint.zero, blendMode: .sourceAtop, alpha: 1.0)

    // On récupére la nouvelle image
    let result = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return result
    }


    a


    Exemple de mise en oeuvre :


    a



    let imageView = UIImageView(frame: self.view.frame)
    self.view.addSubview(imageView)

    if let image1 = UIImage(named: "Montagne") {
    let rect = CGRect(x: image1.size.width/4,
    y: image1.size.height/4,
    width: image1.size.width/2,
    height: image1.size.height/2)
    let image2 = self.cropEllipse(image: image1, rect: rect)
    imageView.image = image2
    }
     

    a


    Copie d'écran du résultat :


     


     


     


     


  • Merci je vais essayer de me pencher dessus, car la comme ça, même si ça a l'air de faire ce que je veux, c'est du chinois, et j'aimerais bien comprendre le code


  • J'ai essayé de regarder et de bidouiller un peu ton code, le souci c'est que c'est une ellipse, alors que moi c'est un cercle que je veut :/


  • Joanna CarterJoanna Carter Membre, Modérateur
    Un circle n'est qu'une ellipse avec les mêmes dimensions.


  • Un circle n'est qu'une ellipse avec les mêmes dimensions.




     


    Une ellipse est ovale, alors qu'un circle est rond, et dans mes specs il me faut un rond ;)

  • Bon j'ai réussi à  faire le cercle, mais ça réponds pas à  mes specs:


     


    - j'ai besoin d'un filtre (avec un rond)


    - et mon image derrière le filtre doit pouvoir être déplacée et zoomée / dézoomée


    - la partie de l'image qui sera gardée est celle qui sera dans le filtre


  • DrakenDraken Membre
    mars 2017 modifié #14


    J'ai essayé de regarder et de bidouiller un peu ton code, le souci c'est que c'est une ellipse, alors que moi c'est un cercle que je veut :/




    a


    L'image est ronde, mais paraà®t déformé parce j'ai forcé le UIImageView à  avoir la même taille que l'écran. Essaye avec ça :



    let image = UIImage(named: "Montagne")
    let imageView = UIImageView(image: image)
    self.view.addSubview(imageView)
    imageView.center = self.view.center

    if let image1 = image {
    let rect = CGRect(x: image1.size.width/4,
    y: image1.size.height/4,
    width: image1.size.width/2,
    height: image1.size.height/2)

    let image2 = self.cropEllipse(image: image1, rect: rect)
    imageView.image = image2
    }




  •  


    a


    L'image est ronde, mais paraà®t déformé parce j'ai forcé le UIImageView à  avoir la même taille que l'écran. Essaye avec ça :



    let image = UIImage(named: "Montagne")
    let imageView = UIImageView(image: image)
    self.view.addSubview(imageView)
    imageView.center = self.view.center

    if let image1 = image {
    let rect = CGRect(x: image1.size.width/4,
    y: image1.size.height/4,
    width: image1.size.width/2,
    height: image1.size.height/2)

    let image2 = self.cropEllipse(image: image1, rect: rect)
    imageView.image = image2
    }




     


    Effectivement j'avais trouvé cette solution et mon crop est désormais rond, mais comme dis ci dessus ça réponds malheureusement pas à  mes specs, et ça me parait difficile de combiner 2 imageView pour n'en garder qu'une seule zone



  • Bon j'ai réussi à  faire le cercle, mais ça réponds pas à  mes specs:


     


    - j'ai besoin d'un filtre (avec un rond)


    - et mon image derrière le filtre doit pouvoir être déplacée et zoomée / dézoomée


    - la partie de l'image qui sera gardée est celle qui sera dans le filtre




     


    Si je comprend bien, le rond est de taille fixe et c'est la photo qui doit bouger et être zoomée ? Cela ne change rien au système de filtrage en lui-même. Je présume que les déplacements se font avec le doigt et les zooms avec un pincement/étirement ?

  • Le rond doit être dans ce rectangle la (position + taille)



    let rect = CGRect(x: 0,
    y: (transformedImage?.size.height)!/4,
    width: (transformedImage?.size.width)!,
    height: (transformedImage?.size.width)!)

    Et effectivement c'est uniquement la photo qui doit être bougée (avec le doigt) et zoomée / dézoomée (avec un pincement / étirement).


     


    Et au final je dois ne garder que la partie de la photo qui se trouvera dans le rond ;)




  • (visiblement ça a déjà  été fait par un collègue en Obj-C mais "ça serait chiant de faire un header-bridge juste pour ça")




    Sérieusement ? Tu as une solution toute faite et c'est un header-bridge qui te bloque ?! 


    Au lieu de chercher comment réinventer la roue regarde plutoÌ‚t comment faire un header-bridge ça te servira à  l'avenir (le reste aussi mais un bon développeur est fainéant).


     


    Le lien sur comment faire. 



  • Sérieusement ? Tu as une solution toute faite et c'est un header-bridge qui te bloque ?! 


    Au lieu de chercher comment réinventer la roue regarde plutoÌ‚t comment faire un header-bridge ça te servira à  l'avenir (le reste aussi mais un bon développeur est fainéant).


     


    Le lien sur comment faire. 




     


    Je te redis ce que m'a dit un collègue


     



     


    Euh en fait tu y passeras beaucoup trop de temps


    C'est pas possible

    On peut faire un bridge pour utiliser de l'Obj-C en Swift mais bon c'est pas rentable pour un pod


     


    Et d'ailleurs je suis en train de voir avec le mec qui a fait le pod en Obj-C il avais mis un équivalent sur Dropbox mais désormais ça renvoie un 404...

  • DrakenDraken Membre
    mars 2017 modifié #20


    Le rond doit être dans ce rectangle la (position + taille)



    let rect = CGRect(x: 0,
    y: (transformedImage?.size.height)!/4,
    width: (transformedImage?.size.width)!,
    height: (transformedImage?.size.width)!)

    Et effectivement c'est uniquement la photo qui doit être bougée (avec le doigt) et zoomée / dézoomée (avec un pincement / étirement).


     


    Et au final je dois ne garder que la partie de la photo qui se trouvera dans le rond ;)




    Tu as besoin de deux filtres :


    - un pour fabriquer ton image en cours d'édition


    - un pour fabriquer l'image finale, en ne gardant que le rond


     


    Voici une version modifiée de mon premier filtre, pour fabriquer l'image en mode édition :



    func cropPartielEllipse(image:UIImage, rect:CGRect) -> UIImage? {
    UIGraphicsBeginImageContext(image.size)
    let context = UIGraphicsGetCurrentContext()

    // Remplissage du fond avec une couleur
    // avec alpha à  30%
    // pour filtrer les pixels en dehors du cercle
    context?.setFillColor(UIColor.white.withAlphaComponent(0.3).cgColor)
    let rectContext = CGRect(x: 0, y: 0,
    width: image.size.width,
    height: image.size.height)
    context?.fill(rectContext)

    // Dessin d'un masque circulaire
    context?.setFillColor(UIColor.white.cgColor)
    context?.fillEllipse(in: rect)

    // Projection de l'image sur le masque
    // avec l'opérateur graphique .sourceAtop
    image.draw(at: CGPoint.zero, blendMode: .sourceAtop, alpha: 1.0)

    // On récupére la nouvelle image
    let result = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return result

    a


    La première version efface tous les pixels en dehors du cercle. La seconde conserve les pixels, avec une transparence de 30% ce qui les rends "anémique".


     


    Résultat en image :


  • Par 2 "filtres" tu entends 2 imageViews ? :)


  • Non !


     


    Par filtre j'entend une méthode trafiquant une UIImage pour en fabriquer une nouvelle.


     


    UIImage -> opération graphique -> nouvelle UIImage


     


    Dans le cas présent :


     


    UIImage Photo -> cropEllipse() -> image photo coupée en cercle


     


    UIImage Photo -> cropPartielEllipse() -> image photo avec cercle bien visible et contour en moins visible


     


    Le UIImageView n'est pas une image, mais sa représentation graphique sur l'écran. C'est juste un visualisateur.

  • Ok je vois merci :)


  • Juste pour rire, on peut cropper une image à  partir de la silhouette en ombre chinoise d'une autre :


    a



    func cropMasque(image:UIImage, masque:UIImage) -> UIImage? {
    UIGraphicsBeginImageContext(image.size)

    // Dessin de l'image source
    // seul le canal alpha est utilisé
    masque.draw(in: CGRect(x: 0, y: 0,
    width: image.size.width,
    height: image.size.height))

    // Projection de l'image sur le masque
    // avec l'opérateur graphique .sourceAtop
    image.draw(at: CGPoint.zero, blendMode: .sourceAtop, alpha: 1.0)

    // On récupére la nouvelle image
    let result = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return result

    f


    Exemples :


     


     


     


  • DrakenDraken Membre
    mars 2017 modifié #25

    Pendant que j'y pense, voici deux extensions de UIImage utiles pour ton projet :



    extension UIImage {
    func decoupage(rect:CGRect) -> UIImage? {
    if let cgimage = self.cgImage {
    if let cgimage2 = cgimage.cropping(to: rect) {
    return UIImage(cgImage: cgimage2)
    }
    }
    return nil
    }

    func resize(size:CGSize) -> UIImage? {
    UIGraphicsBeginImageContext(size)
    self.draw(in:CGRect(origin: CGPoint.zero, size: size))
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
    }
    }


    - decoupage() permet d'extraire une partie d'une image.



    let image = UIImage(..) // Image originale
    let rect = CGRect(..) // Portion à  découper

    let portionImage = image.decoupage(rect)


    - resize() crée une copie de l'image plus grande ou plus petite que l'original.



    let image = UIImage (..) // Image d'origine
    let newSize = CGSize(..) // Nouvelle taille

    let imageResize = image.resize(newSize)


  • Merci pour ton investissement Draken, tu gères ;)




  • Merci pour ton investissement Draken, tu gères ;)




     


    L'investissement n'est pas bien lourd. J'avais déjà  tout sous la main. 

  • Ca n'empêche quand même, qu'un merci c'est un minimum, tu te démènes (même si j'ai bien compris que c'était déjà  fait) depuis hier pour m'aider ;)


     


    Je vais regarder ta solution BTW :D


     


    Et aussi si ça peut intéresser du monde: le pod Obj-C que mon collègue utilisait est de nouveau dispo en swift sur Dropbox


     


    Lien ici : https://github.com/ruslanskorb/RSKImageCropper/issues/120


     


    Je vais y jeter aussi un oeil :)


  • Ben77650Ben77650 Membre
    mars 2017 modifié #29

    Résolu en utilisant le pod RSKImageCropper.


     


    Cependant j'ai quand même ajouté ton extension Image Draken, qui sait, ça pourrait me servir :)


  • Good ! 


     


    Au fait, mon système fonctionne aussi avec l'opérateur graphique BlendMode.sourceIn dont l'équation est plus simple à  comprendre :


     


    S = image Source


    Da = canal alpha de l'image présente sur la Destination


    R = Résultat final du mélange des deux images


     


    R = S*Da



  • Good ! 


     


    Au fait, mon système fonctionne aussi avec l'opérateur graphique BlendMode.sourceIn dont l'équation est plus simple à  comprendre :


     


    S = image Source


    Da = canal alpha de l'image présente sur la Destination


    R = Résultat final du mélange des deux images


     


    R = S*Da




     


    C'est pas faux  ;D


     


    Merci en tout cas

Connectez-vous ou Inscrivez-vous pour répondre.