diff --git a/src/frontend/public/assets/beret.png b/src/frontend/public/assets/beret.png new file mode 100644 index 00000000..f9691793 Binary files /dev/null and b/src/frontend/public/assets/beret.png differ diff --git a/src/frontend/src/features/rooms/livekit/components/blur/FaceLandmarksProcessor.ts b/src/frontend/src/features/rooms/livekit/components/blur/FaceLandmarksProcessor.ts index 462ce624..ef4f51d3 100644 --- a/src/frontend/src/features/rooms/livekit/components/blur/FaceLandmarksProcessor.ts +++ b/src/frontend/src/features/rooms/livekit/components/blur/FaceLandmarksProcessor.ts @@ -49,7 +49,7 @@ export class FaceLandmarksProcessor implements BackgroundProcessorInterface { // Effect images glassesImage?: HTMLImageElement mustacheImage?: HTMLImageElement - + beretImage?: HTMLImageElement constructor(opts: BackgroundOptions) { this.name = 'face_landmarks' this.options = opts @@ -65,6 +65,10 @@ export class FaceLandmarksProcessor implements BackgroundProcessorInterface { this.mustacheImage = new Image() this.mustacheImage.src = '/assets/mustache.png' this.mustacheImage.crossOrigin = 'anonymous' + + this.beretImage = new Image() + this.beretImage.src = '/assets/beret.png' + this.beretImage.crossOrigin = 'anonymous' } static get isSupported() { @@ -172,7 +176,8 @@ export class FaceLandmarksProcessor implements BackgroundProcessorInterface { rightPoint: { x: number; y: number }, image: HTMLImageElement, widthScale: number, - heightScale: number + heightScale: number, + yOffset: number = 0 ) { // Calculate distance between points const distance = Math.sqrt( @@ -186,7 +191,7 @@ export class FaceLandmarksProcessor implements BackgroundProcessorInterface { // Calculate center position between points const centerX = (leftPoint.x + rightPoint.x) / 2 - const centerY = (leftPoint.y + rightPoint.y) / 2 + const centerY = (leftPoint.y + rightPoint.y) / 2 + yOffset // Draw image this.outputCanvasCtx!.save() @@ -237,21 +242,29 @@ export class FaceLandmarksProcessor implements BackgroundProcessorInterface { this.outputCanvasCtx!.lineWidth = 2 for (const face of this.faceLandmarkerResult.faceLandmarks) { - // Find eye landmarks (indices 33 and 263 are the left and right eye corners) + // Find eye landmarks const leftEye = face[468] const rightEye = face[473] - // Find mouth landmarks for mustache (indices 0 and 17 are the left and right corners of the mouth) + // Find mouth landmarks for mustache const leftMoustache = face[92] const rightMoustache = face[322] + // Find forehead landmarks for beret + const leftForehead = face[103] + const rightForehead = face[332] + if (leftEye && rightEye && this.options.showGlasses) { this.drawEffect(leftEye, rightEye, this.glassesImage!, 2.5, 0.7) } - if (leftMoustache && rightMoustache && this.options.showMustache) { + if (leftMoustache && rightMoustache && this.options.showFrench) { this.drawEffect(leftMoustache, rightMoustache, this.mustacheImage!, 1.5, 0.5) } + + if (leftForehead && rightForehead && this.options.showFrench) { + this.drawEffect(leftForehead, rightForehead, this.beretImage!, 2.1, 0.7, -0.1) + } } } diff --git a/src/frontend/src/features/rooms/livekit/components/blur/index.ts b/src/frontend/src/features/rooms/livekit/components/blur/index.ts index e7f27414..0b345d4a 100644 --- a/src/frontend/src/features/rooms/livekit/components/blur/index.ts +++ b/src/frontend/src/features/rooms/livekit/components/blur/index.ts @@ -9,7 +9,7 @@ export type BackgroundOptions = { blurRadius?: number imagePath?: string showGlasses?: boolean - showMustache?: boolean + showFrench?: boolean } export interface ProcessorSerialized { diff --git a/src/frontend/src/features/rooms/livekit/components/effects/EffectsConfiguration.tsx b/src/frontend/src/features/rooms/livekit/components/effects/EffectsConfiguration.tsx index 10e19722..4b9dd996 100644 --- a/src/frontend/src/features/rooms/livekit/components/effects/EffectsConfiguration.tsx +++ b/src/frontend/src/features/rooms/livekit/components/effects/EffectsConfiguration.tsx @@ -15,7 +15,7 @@ import { BlurOnStrong } from '@/components/icons/BlurOnStrong' import { useTrackToggle } from '@livekit/components-react' import { Loader } from '@/primitives/Loader' import { useSyncAfterDelay } from '@/hooks/useSyncAfterDelay' -import { RiProhibited2Line, RiGlassesLine, RiEmotionLine } from '@remixicon/react' +import { RiProhibited2Line, RiGlassesLine, RiGoblet2Fill } from '@remixicon/react' import { useHasFaceLandmarksAccess } from '../../hooks/useHasFaceLandmarksAccess' enum BlurRadius { @@ -142,7 +142,7 @@ export const EffectsConfiguration = ({ const tooltipLabel = (type: ProcessorType, options: BackgroundOptions) => { if (type === ProcessorType.FACE_LANDMARKS) { - const effect = options.showGlasses ? 'glasses' : 'mustache' + const effect = options.showGlasses ? 'glasses' : 'french' return t(`faceLandmarks.${effect}.${isSelected(type, options) ? 'clear' : 'apply'}`) } return t(`${type}.${isSelected(type, options) ? 'clear' : 'apply'}`) @@ -151,19 +151,19 @@ export const EffectsConfiguration = ({ const getFaceLandmarksOptions = () => { const processor = getProcessor() if (processor?.serialize().type === ProcessorType.FACE_LANDMARKS) { - return processor.serialize().options as { showGlasses?: boolean; showMustache?: boolean } + return processor.serialize().options as { showGlasses?: boolean; showFrench?: boolean } } - return { showGlasses: false, showMustache: false } + return { showGlasses: false, showFrench: false } } - const toggleFaceLandmarkEffect = async (effect: 'glasses' | 'mustache') => { + const toggleFaceLandmarkEffect = async (effect: 'glasses' | 'french') => { const currentOptions = getFaceLandmarksOptions() const newOptions = { ...currentOptions, - [effect === 'glasses' ? 'showGlasses' : 'showMustache']: !currentOptions[effect === 'glasses' ? 'showGlasses' : 'showMustache'] + [effect === 'glasses' ? 'showGlasses' : 'showFrench']: !currentOptions[effect === 'glasses' ? 'showGlasses' : 'showFrench'] } - if (!newOptions.showGlasses && !newOptions.showMustache) { + if (!newOptions.showGlasses && !newOptions.showFrench) { // If both effects are off stop the processor await clearEffect() } else { @@ -356,11 +356,11 @@ export const EffectsConfiguration = ({ variant="bigSquare" aria-label={tooltipLabel(ProcessorType.FACE_LANDMARKS, { showGlasses: true, - showMustache: false, + showFrench: false, })} tooltip={tooltipLabel(ProcessorType.FACE_LANDMARKS, { showGlasses: true, - showMustache: false, + showFrench: false, })} isDisabled={processorPendingReveal} onChange={async () => await toggleFaceLandmarkEffect('glasses')} @@ -373,18 +373,18 @@ export const EffectsConfiguration = ({ variant="bigSquare" aria-label={tooltipLabel(ProcessorType.FACE_LANDMARKS, { showGlasses: false, - showMustache: true, + showFrench: true, })} tooltip={tooltipLabel(ProcessorType.FACE_LANDMARKS, { showGlasses: false, - showMustache: true, + showFrench: true, })} isDisabled={processorPendingReveal} - onChange={async () => await toggleFaceLandmarkEffect('mustache')} - isSelected={getFaceLandmarksOptions().showMustache} - data-attr="toggle-mustache" + onChange={async () => await toggleFaceLandmarkEffect('french')} + isSelected={getFaceLandmarksOptions().showFrench} + data-attr="toggle-french" > - + diff --git a/src/frontend/src/locales/de/rooms.json b/src/frontend/src/locales/de/rooms.json index 2a672fd7..38aa76f0 100644 --- a/src/frontend/src/locales/de/rooms.json +++ b/src/frontend/src/locales/de/rooms.json @@ -156,9 +156,9 @@ "apply": "Brille hinzufügen", "clear": "Brille entfernen" }, - "mustache": { - "apply": "Schnurrbart hinzufügen", - "clear": "Schnurrbart entfernen" + "french": { + "apply": "Französische Touch hinzufügen", + "clear": "Französische Touch entfernen" } }, "experimental": "Experimentelle Funktion. Eine v2 kommt für vollständige Browserunterstützung und verbesserte Qualität." diff --git a/src/frontend/src/locales/en/rooms.json b/src/frontend/src/locales/en/rooms.json index 23fc147d..ca125a14 100644 --- a/src/frontend/src/locales/en/rooms.json +++ b/src/frontend/src/locales/en/rooms.json @@ -155,9 +155,9 @@ "apply": "Add Glasses", "clear": "Remove Glasses" }, - "mustache": { - "apply": "Add Mustache", - "clear": "Remove Mustache" + "french": { + "apply": "Add French touch", + "clear": "Remove French touch" } }, "experimental": "Experimental feature. A v2 is coming for full browser support and improved quality." diff --git a/src/frontend/src/locales/fr/rooms.json b/src/frontend/src/locales/fr/rooms.json index 0508179c..2ac71f08 100644 --- a/src/frontend/src/locales/fr/rooms.json +++ b/src/frontend/src/locales/fr/rooms.json @@ -155,9 +155,9 @@ "apply": "Ajouter des lunettes", "clear": "Retirer les lunettes" }, - "mustache": { - "apply": "Ajouter une moustache", - "clear": "Retirer la moustache" + "french": { + "apply": "Ajouter la touche française", + "clear": "Retirer la touche française" } }, "experimental": "Fonctionnalité expérimentale. Une v2 arrive pour un support complet des navigateurs et une meilleure qualité." diff --git a/src/frontend/src/locales/nl/rooms.json b/src/frontend/src/locales/nl/rooms.json index da56ae02..7f38350a 100644 --- a/src/frontend/src/locales/nl/rooms.json +++ b/src/frontend/src/locales/nl/rooms.json @@ -155,9 +155,9 @@ "apply": "Bril toevoegen", "clear": "Bril verwijderen" }, - "mustache": { - "apply": "Snor toevoegen", - "clear": "Snor verwijderen" + "french": { + "apply": "Franse stijl toevoegen", + "clear": "Franse stijl verwijderen" } }, "experimental": "Experimentele functie. Een v2 komt eraan voor volledige browserondersteuning en verbeterde kwaliteit."