jbilcke-hf HF staff commited on
Commit
8378233
Β·
1 Parent(s): 0a5e214

avoid hash collision between users using the same image

Browse files
client/src/hooks/useMainStore.ts CHANGED
@@ -18,7 +18,7 @@ export type ImageState = ImageStateValues & {
18
  setIsFollowingCursor: (isFollowingCursor: boolean) => void
19
  setIsGazingAtCursor: (isGazingAtCursor: boolean) => void
20
  setOriginalImage: (url: string) => void
21
- setOriginalImageHash: (hash: string) => void
22
  setPreviewImage: (url: string) => void
23
  resetImage: () => void
24
  setAverageLatency: (averageLatency: number) => void
@@ -41,7 +41,7 @@ export const getDefaultState = (): ImageStateValues => ({
41
  isFollowingCursor: false,
42
  isGazingAtCursor: false,
43
  originalImage: '',
44
- originalImageHash: '',
45
  previewImage: '',
46
  minLatency: 20, // min time between requests
47
  averageLatency: 190, // this should be the average for most people
@@ -98,7 +98,7 @@ export const useMainStore = create<ImageState>((set, get) => ({
98
  setIsFollowingCursor: (isFollowingCursor: boolean) => set({ isFollowingCursor }),
99
  setIsGazingAtCursor: (isGazingAtCursor: boolean) => set({ isGazingAtCursor }),
100
  setOriginalImage: (url) => set({ originalImage: url }),
101
- setOriginalImageHash: (originalImageHash) => set({ originalImageHash }),
102
  setPreviewImage: (url) => set({ previewImage: url }),
103
  resetImage: () => {
104
  const { originalImage } = get()
@@ -121,11 +121,11 @@ export const useMainStore = create<ImageState>((set, get) => ({
121
  }})
122
  },
123
  handleServerResponse: async (params: OnServerResponseParams) => {
124
- const { originalImage, setMetadata, setPreviewImage, setOriginalImageHash, applyModifiedHeadToCanvas, modifyImage } = useMainStore.getState();
125
  if (typeof params.error === "string") {
126
  console.error(`handleServerResponse: failed to perform the request, resetting the app (${params.error})`)
127
  setPreviewImage(originalImage)
128
- setOriginalImageHash('')
129
  } else if (typeof params.image !== "undefined") {
130
 
131
  // this is where we decide to paste back the image as a whole,
@@ -141,7 +141,7 @@ export const useMainStore = create<ImageState>((set, get) => ({
141
  setPreviewImage(image);
142
  } else if (typeof params.loaded !== "undefined") {
143
  //console.log(`handleServerResponse: received a json`, params)
144
- setOriginalImageHash(params.loaded.h)
145
  setMetadata({
146
  center: params.loaded.c, // center - 2x1
147
  size: params.loaded.s, // size - scalar
@@ -149,7 +149,7 @@ export const useMainStore = create<ImageState>((set, get) => ({
149
  angle: params.loaded.a, //angle - rad, counterclockwise
150
  })
151
 
152
- // right after we received the hash, we perform a first blank request
153
  await modifyImage({
154
  landmark: {
155
  group: 'background',
@@ -264,7 +264,7 @@ export const useMainStore = create<ImageState>((set, get) => ({
264
 
265
  const {
266
  originalImage,
267
- originalImageHash,
268
  params: previousParams,
269
  setParams,
270
  setError,
@@ -424,8 +424,8 @@ export const useMainStore = create<ImageState>((set, get) => ({
424
 
425
  try {
426
 
427
- if (originalImageHash) {
428
- facePoke.transformImage(originalImageHash, params);
429
  }
430
 
431
  } catch (error) {
 
18
  setIsFollowingCursor: (isFollowingCursor: boolean) => void
19
  setIsGazingAtCursor: (isGazingAtCursor: boolean) => void
20
  setOriginalImage: (url: string) => void
21
+ setOriginalImageUuid: (uuid: string) => void
22
  setPreviewImage: (url: string) => void
23
  resetImage: () => void
24
  setAverageLatency: (averageLatency: number) => void
 
41
  isFollowingCursor: false,
42
  isGazingAtCursor: false,
43
  originalImage: '',
44
+ originalImageUuid: '',
45
  previewImage: '',
46
  minLatency: 20, // min time between requests
47
  averageLatency: 190, // this should be the average for most people
 
98
  setIsFollowingCursor: (isFollowingCursor: boolean) => set({ isFollowingCursor }),
99
  setIsGazingAtCursor: (isGazingAtCursor: boolean) => set({ isGazingAtCursor }),
100
  setOriginalImage: (url) => set({ originalImage: url }),
101
+ setOriginalImageUuid: (originalImageUuid) => set({ originalImageUuid }),
102
  setPreviewImage: (url) => set({ previewImage: url }),
103
  resetImage: () => {
104
  const { originalImage } = get()
 
121
  }})
122
  },
123
  handleServerResponse: async (params: OnServerResponseParams) => {
124
+ const { originalImage, setMetadata, setPreviewImage, setOriginalImageUuid, applyModifiedHeadToCanvas, modifyImage } = useMainStore.getState();
125
  if (typeof params.error === "string") {
126
  console.error(`handleServerResponse: failed to perform the request, resetting the app (${params.error})`)
127
  setPreviewImage(originalImage)
128
+ setOriginalImageUuid('')
129
  } else if (typeof params.image !== "undefined") {
130
 
131
  // this is where we decide to paste back the image as a whole,
 
141
  setPreviewImage(image);
142
  } else if (typeof params.loaded !== "undefined") {
143
  //console.log(`handleServerResponse: received a json`, params)
144
+ setOriginalImageUuid(params.loaded.u)
145
  setMetadata({
146
  center: params.loaded.c, // center - 2x1
147
  size: params.loaded.s, // size - scalar
 
149
  angle: params.loaded.a, //angle - rad, counterclockwise
150
  })
151
 
152
+ // right after we received the id, we perform a first blank request
153
  await modifyImage({
154
  landmark: {
155
  group: 'background',
 
264
 
265
  const {
266
  originalImage,
267
+ originalImageUuid,
268
  params: previousParams,
269
  setParams,
270
  setError,
 
424
 
425
  try {
426
 
427
+ if (originalImageUuid) {
428
+ facePoke.transformImage(originalImageUuid, params);
429
  }
430
 
431
  } catch (error) {
client/src/lib/facePoke.ts CHANGED
@@ -135,8 +135,8 @@ export class FacePoke {
135
  this.sendBlobMessage(await blob.arrayBuffer());
136
  }
137
 
138
- public transformImage(hash: string, params: Partial<ImageModificationParams>): void {
139
- this.sendJsonMessage({ hash, params });
140
  }
141
 
142
  private sendBlobMessage(buffer: ArrayBuffer): void {
 
135
  this.sendBlobMessage(await blob.arrayBuffer());
136
  }
137
 
138
+ public transformImage(uuid: string, params: Partial<ImageModificationParams>): void {
139
+ this.sendJsonMessage({ uuid, params });
140
  }
141
 
142
  private sendBlobMessage(buffer: ArrayBuffer): void {
client/src/types.ts CHANGED
@@ -30,7 +30,7 @@ export interface Metadata {
30
  */
31
  export interface ModifyImageMessage {
32
  image?: string;
33
- hash?: string;
34
  params: Partial<ImageModificationParams>;
35
  }
36
 
@@ -38,7 +38,7 @@ export type OnServerResponseParams = {
38
  image?: Blob
39
  error?: string
40
  loaded?: {
41
- h: string
42
  } & {
43
  c: number[] //center - 2x1
44
  s: number // size - scalar
@@ -80,7 +80,7 @@ export interface ImageStateValues {
80
  isGazingAtCursor: boolean
81
  originalImage: string
82
  previewImage: string
83
- originalImageHash: string
84
  minLatency: number
85
  averageLatency: number
86
  maxLatency: number
 
30
  */
31
  export interface ModifyImageMessage {
32
  image?: string;
33
+ uuid?: string;
34
  params: Partial<ImageModificationParams>;
35
  }
36
 
 
38
  image?: Blob
39
  error?: string
40
  loaded?: {
41
+ i: string
42
  } & {
43
  c: number[] //center - 2x1
44
  s: number // size - scalar
 
80
  isGazingAtCursor: boolean
81
  originalImage: string
82
  previewImage: string
83
+ originalImageUuid: string
84
  minLatency: number
85
  averageLatency: number
86
  maxLatency: number
engine.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import logging
2
  import hashlib
3
  import os
@@ -61,37 +62,10 @@ class Engine:
61
 
62
  logger.info("βœ… FacePoke Engine initialized successfully.")
63
 
64
- async def get_image_hash(self, image: Union[Image.Image, str, bytes]) -> str:
65
- """
66
- Compute or retrieve the hash for an image.
67
-
68
- Args:
69
- image (Union[Image.Image, str, bytes]): The input image, either as a PIL Image,
70
- base64 string, or bytes.
71
-
72
- Returns:
73
- str: The computed hash of the image.
74
- """
75
- if isinstance(image, str):
76
- # Assume it's already a hash if it's a string of the right length
77
- if len(image) == 32:
78
- return image
79
- # Otherwise, assume it's a base64 string
80
- image = base64_data_uri_to_PIL_Image(image)
81
-
82
- if isinstance(image, Image.Image):
83
- return hashlib.md5(image.tobytes()).hexdigest()
84
- elif isinstance(image, bytes):
85
- return hashlib.md5(image).hexdigest()
86
- else:
87
- raise ValueError("Unsupported image type")
88
-
89
  @alru_cache(maxsize=512)
90
  async def load_image(self, data):
91
  image = Image.open(io.BytesIO(data))
92
-
93
- image_hash = await self.get_image_hash(image)
94
-
95
  img_rgb = np.array(image)
96
 
97
  inference_cfg = self.live_portrait.live_portrait_wrapper.cfg
@@ -113,13 +87,13 @@ class Engine:
113
  'inference_cfg': inference_cfg
114
  }
115
 
116
- self.processed_cache[image_hash] = processed_data
117
 
118
  # Calculate the bounding box
119
  bbox_info = parse_bbox_from_landmark(processed_data['crop_info']['lmk_crop'], scale=1.0)
120
 
121
  return {
122
- 'h': image_hash,
123
 
124
  # those aren't easy to serialize
125
  'c': bbox_info['center'], # 2x1
@@ -129,12 +103,12 @@ class Engine:
129
  # 'bbox_rot': bbox_info['bbox_rot'].toList(), # 4x2
130
  }
131
 
132
- async def transform_image(self, image_hash: str, params: Dict[str, float]) -> bytes:
133
  # If we don't have the image in cache yet, add it
134
- if image_hash not in self.processed_cache:
135
  raise ValueError("cache miss")
136
 
137
- processed_data = self.processed_cache[image_hash]
138
 
139
  try:
140
  # Apply modifications based on params
 
1
+ import uuid
2
  import logging
3
  import hashlib
4
  import os
 
62
 
63
  logger.info("βœ… FacePoke Engine initialized successfully.")
64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  @alru_cache(maxsize=512)
66
  async def load_image(self, data):
67
  image = Image.open(io.BytesIO(data))
68
+ uuid = uuid.uuid4()
 
 
69
  img_rgb = np.array(image)
70
 
71
  inference_cfg = self.live_portrait.live_portrait_wrapper.cfg
 
87
  'inference_cfg': inference_cfg
88
  }
89
 
90
+ self.processed_cache[uuid] = processed_data
91
 
92
  # Calculate the bounding box
93
  bbox_info = parse_bbox_from_landmark(processed_data['crop_info']['lmk_crop'], scale=1.0)
94
 
95
  return {
96
+ 'u': uuid,
97
 
98
  # those aren't easy to serialize
99
  'c': bbox_info['center'], # 2x1
 
103
  # 'bbox_rot': bbox_info['bbox_rot'].toList(), # 4x2
104
  }
105
 
106
+ async def transform_image(self, uuid: str, params: Dict[str, float]) -> bytes:
107
  # If we don't have the image in cache yet, add it
108
+ if uuid not in self.processed_cache:
109
  raise ValueError("cache miss")
110
 
111
+ processed_data = self.processed_cache[uuid]
112
 
113
  try:
114
  # Apply modifications based on params
public/index.js CHANGED
@@ -29754,8 +29754,8 @@ class FacePoke {
29754
  const blob = new Blob([buffer], { type: "application/octet-binary" });
29755
  this.sendBlobMessage(await blob.arrayBuffer());
29756
  }
29757
- transformImage(hash, params) {
29758
- this.sendJsonMessage({ hash, params });
29759
  }
29760
  sendBlobMessage(buffer) {
29761
  if (!this.ws || this.ws.readyState !== WebSocketState.OPEN) {
@@ -29888,7 +29888,7 @@ var getDefaultState = () => ({
29888
  isFollowingCursor: false,
29889
  isGazingAtCursor: false,
29890
  originalImage: "",
29891
- originalImageHash: "",
29892
  previewImage: "",
29893
  minLatency: 20,
29894
  averageLatency: 190,
@@ -29943,7 +29943,7 @@ var useMainStore = create((set, get) => ({
29943
  setIsFollowingCursor: (isFollowingCursor) => set({ isFollowingCursor }),
29944
  setIsGazingAtCursor: (isGazingAtCursor) => set({ isGazingAtCursor }),
29945
  setOriginalImage: (url) => set({ originalImage: url }),
29946
- setOriginalImageHash: (originalImageHash) => set({ originalImageHash }),
29947
  setPreviewImage: (url) => set({ previewImage: url }),
29948
  resetImage: () => {
29949
  const { originalImage } = get();
@@ -29966,16 +29966,16 @@ var useMainStore = create((set, get) => ({
29966
  } });
29967
  },
29968
  handleServerResponse: async (params) => {
29969
- const { originalImage, setMetadata, setPreviewImage, setOriginalImageHash, applyModifiedHeadToCanvas, modifyImage } = useMainStore.getState();
29970
  if (typeof params.error === "string") {
29971
  console.error(`handleServerResponse: failed to perform the request, resetting the app (${params.error})`);
29972
  setPreviewImage(originalImage);
29973
- setOriginalImageHash("");
29974
  } else if (typeof params.image !== "undefined") {
29975
  const image = await convertImageToBase64(params.image);
29976
  setPreviewImage(image);
29977
  } else if (typeof params.loaded !== "undefined") {
29978
- setOriginalImageHash(params.loaded.h);
29979
  setMetadata({
29980
  center: params.loaded.c,
29981
  size: params.loaded.s,
@@ -30041,7 +30041,7 @@ var useMainStore = create((set, get) => ({
30041
  modifyImage: async ({ landmark, vector, mode }) => {
30042
  const {
30043
  originalImage,
30044
- originalImageHash,
30045
  params: previousParams,
30046
  setParams,
30047
  setError,
@@ -30139,8 +30139,8 @@ var useMainStore = create((set, get) => ({
30139
  }
30140
  setParams(params);
30141
  try {
30142
- if (originalImageHash) {
30143
- facePoke.transformImage(originalImageHash, params);
30144
  }
30145
  } catch (error) {
30146
  setError("Failed to modify image");
 
29754
  const blob = new Blob([buffer], { type: "application/octet-binary" });
29755
  this.sendBlobMessage(await blob.arrayBuffer());
29756
  }
29757
+ transformImage(uuid, params) {
29758
+ this.sendJsonMessage({ uuid, params });
29759
  }
29760
  sendBlobMessage(buffer) {
29761
  if (!this.ws || this.ws.readyState !== WebSocketState.OPEN) {
 
29888
  isFollowingCursor: false,
29889
  isGazingAtCursor: false,
29890
  originalImage: "",
29891
+ originalImageUuid: "",
29892
  previewImage: "",
29893
  minLatency: 20,
29894
  averageLatency: 190,
 
29943
  setIsFollowingCursor: (isFollowingCursor) => set({ isFollowingCursor }),
29944
  setIsGazingAtCursor: (isGazingAtCursor) => set({ isGazingAtCursor }),
29945
  setOriginalImage: (url) => set({ originalImage: url }),
29946
+ setOriginalImageUuid: (originalImageUuid) => set({ originalImageUuid }),
29947
  setPreviewImage: (url) => set({ previewImage: url }),
29948
  resetImage: () => {
29949
  const { originalImage } = get();
 
29966
  } });
29967
  },
29968
  handleServerResponse: async (params) => {
29969
+ const { originalImage, setMetadata, setPreviewImage, setOriginalImageUuid, applyModifiedHeadToCanvas, modifyImage } = useMainStore.getState();
29970
  if (typeof params.error === "string") {
29971
  console.error(`handleServerResponse: failed to perform the request, resetting the app (${params.error})`);
29972
  setPreviewImage(originalImage);
29973
+ setOriginalImageUuid("");
29974
  } else if (typeof params.image !== "undefined") {
29975
  const image = await convertImageToBase64(params.image);
29976
  setPreviewImage(image);
29977
  } else if (typeof params.loaded !== "undefined") {
29978
+ setOriginalImageUuid(params.loaded.u);
29979
  setMetadata({
29980
  center: params.loaded.c,
29981
  size: params.loaded.s,
 
30041
  modifyImage: async ({ landmark, vector, mode }) => {
30042
  const {
30043
  originalImage,
30044
+ originalImageUuid,
30045
  params: previousParams,
30046
  setParams,
30047
  setError,
 
30139
  }
30140
  setParams(params);
30141
  try {
30142
+ if (originalImageUuid) {
30143
+ facePoke.transformImage(originalImageUuid, params);
30144
  }
30145
  } catch (error) {
30146
  setError("Failed to modify image");