fal 1.2.2__tar.gz → 1.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fal might be problematic. Click here for more details.
- {fal-1.2.2 → fal-1.2.3}/PKG-INFO +1 -1
- {fal-1.2.2 → fal-1.2.3}/fal.egg-info/PKG-INFO +1 -1
- {fal-1.2.2 → fal-1.2.3}/fal.egg-info/SOURCES.txt +6 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/_fal_version.py +2 -2
- {fal-1.2.2 → fal-1.2.3}/src/fal/app.py +1 -1
- fal-1.2.3/src/fal/toolkit/image/__init__.py +74 -0
- fal-1.2.3/src/fal/toolkit/image/nsfw_filter/__init__.py +11 -0
- fal-1.2.3/src/fal/toolkit/image/nsfw_filter/env.py +9 -0
- fal-1.2.3/src/fal/toolkit/image/nsfw_filter/inference.py +77 -0
- fal-1.2.3/src/fal/toolkit/image/nsfw_filter/model.py +18 -0
- fal-1.2.3/src/fal/toolkit/image/nsfw_filter/requirements.txt +4 -0
- fal-1.2.3/src/fal/toolkit/image/safety_checker.py +107 -0
- fal-1.2.2/src/fal/toolkit/image/__init__.py +0 -3
- {fal-1.2.2 → fal-1.2.3}/.gitignore +0 -0
- {fal-1.2.2 → fal-1.2.3}/README.md +0 -0
- {fal-1.2.2 → fal-1.2.3}/fal.egg-info/dependency_links.txt +0 -0
- {fal-1.2.2 → fal-1.2.3}/fal.egg-info/entry_points.txt +0 -0
- {fal-1.2.2 → fal-1.2.3}/fal.egg-info/requires.txt +0 -0
- {fal-1.2.2 → fal-1.2.3}/fal.egg-info/top_level.txt +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/README.md +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/pyproject.toml +0 -0
- {fal-1.2.2 → fal-1.2.3}/openapi_rest.config.yaml +0 -0
- {fal-1.2.2 → fal-1.2.3}/pyproject.toml +0 -0
- {fal-1.2.2 → fal-1.2.3}/setup.cfg +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/__main__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/_serialization.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/_version.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/api.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/apps.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/auth/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/auth/auth0.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/auth/local.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/cli/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/cli/apps.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/cli/auth.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/cli/create.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/cli/debug.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/cli/deploy.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/cli/doctor.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/cli/keys.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/cli/main.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/cli/parser.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/cli/run.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/cli/secrets.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/console/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/console/icons.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/console/ux.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/container.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/exceptions/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/exceptions/_base.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/exceptions/auth.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/flags.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/logging/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/logging/isolate.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/logging/style.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/logging/trace.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/logging/user.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/py.typed +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/rest_client.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/sdk.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/sync.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/toolkit/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/toolkit/exceptions.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/toolkit/file/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/toolkit/file/file.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/toolkit/file/providers/fal.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/toolkit/file/providers/gcp.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/toolkit/file/providers/r2.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/toolkit/file/types.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/toolkit/image/image.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/toolkit/optimize.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/toolkit/utils/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/toolkit/utils/download_utils.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/utils.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/src/fal/workflows.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/cli/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/cli/test_apps.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/cli/test_auth.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/cli/test_deploy.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/cli/test_keys.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/cli/test_run.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/cli/test_secrets.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/conftest.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/integration_test.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/mainify_package/__init__.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/mainify_package/impl.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/mainify_package/utils.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/mainify_target.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/test_apps.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/test_stability.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/toolkit/file_test.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tests/toolkit/image_test.py +0 -0
- {fal-1.2.2 → fal-1.2.3}/tools/demo_script.py +0 -0
{fal-1.2.2 → fal-1.2.3}/PKG-INFO
RENAMED
|
@@ -126,6 +126,12 @@ src/fal/toolkit/file/providers/gcp.py
|
|
|
126
126
|
src/fal/toolkit/file/providers/r2.py
|
|
127
127
|
src/fal/toolkit/image/__init__.py
|
|
128
128
|
src/fal/toolkit/image/image.py
|
|
129
|
+
src/fal/toolkit/image/safety_checker.py
|
|
130
|
+
src/fal/toolkit/image/nsfw_filter/__init__.py
|
|
131
|
+
src/fal/toolkit/image/nsfw_filter/env.py
|
|
132
|
+
src/fal/toolkit/image/nsfw_filter/inference.py
|
|
133
|
+
src/fal/toolkit/image/nsfw_filter/model.py
|
|
134
|
+
src/fal/toolkit/image/nsfw_filter/requirements.txt
|
|
129
135
|
src/fal/toolkit/utils/__init__.py
|
|
130
136
|
src/fal/toolkit/utils/download_utils.py
|
|
131
137
|
tests/__init__.py
|
|
@@ -158,7 +158,7 @@ class App(fal.api.BaseServable):
|
|
|
158
158
|
app_name = kwargs.pop("name", None) or _to_fal_app_name(cls.__name__)
|
|
159
159
|
parent_settings = getattr(cls, "host_kwargs", {})
|
|
160
160
|
cls.host_kwargs = {**parent_settings, **kwargs}
|
|
161
|
-
cls.app_name = app_name
|
|
161
|
+
cls.app_name = getattr(cls, "app_name", app_name)
|
|
162
162
|
|
|
163
163
|
if cls.__init__ is not App.__init__:
|
|
164
164
|
raise ValueError(
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from functools import lru_cache
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
from urllib.request import Request, urlopen
|
|
6
|
+
|
|
7
|
+
from .image import * # noqa: F403
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from PIL.Image import Image as PILImage
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def filter_by(
|
|
14
|
+
has_nsfw_concepts: list[bool],
|
|
15
|
+
images: list[PILImage],
|
|
16
|
+
) -> list[PILImage]:
|
|
17
|
+
from PIL import Image as PILImage
|
|
18
|
+
|
|
19
|
+
return [
|
|
20
|
+
(
|
|
21
|
+
PILImage.new("RGB", (image.width, image.height), (0, 0, 0))
|
|
22
|
+
if has_nsfw
|
|
23
|
+
else image
|
|
24
|
+
)
|
|
25
|
+
for image, has_nsfw in zip(images, has_nsfw_concepts)
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def preprocess_image(image_pil, convert_to_rgb=True, fix_orientation=True):
|
|
30
|
+
from PIL import ImageOps, ImageSequence
|
|
31
|
+
|
|
32
|
+
# For MPO (multi picture object) format images, we only need the first image
|
|
33
|
+
images = []
|
|
34
|
+
for image in ImageSequence.Iterator(image_pil):
|
|
35
|
+
img = image
|
|
36
|
+
|
|
37
|
+
if convert_to_rgb:
|
|
38
|
+
img = img.convert("RGB")
|
|
39
|
+
|
|
40
|
+
if fix_orientation:
|
|
41
|
+
img = ImageOps.exif_transpose(img)
|
|
42
|
+
|
|
43
|
+
images.append(img)
|
|
44
|
+
|
|
45
|
+
break
|
|
46
|
+
|
|
47
|
+
return images[0]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@lru_cache(maxsize=64)
|
|
51
|
+
def read_image_from_url(
|
|
52
|
+
url: str, convert_to_rgb: bool = True, fix_orientation: bool = True
|
|
53
|
+
):
|
|
54
|
+
from fastapi import HTTPException
|
|
55
|
+
from PIL import Image
|
|
56
|
+
|
|
57
|
+
TEMP_HEADERS = {
|
|
58
|
+
"User-Agent": (
|
|
59
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:21.0) "
|
|
60
|
+
"Gecko/20100101 Firefox/21.0"
|
|
61
|
+
),
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
request = Request(url, headers=TEMP_HEADERS)
|
|
66
|
+
response = urlopen(request)
|
|
67
|
+
image_pil = Image.open(response)
|
|
68
|
+
except Exception:
|
|
69
|
+
import traceback
|
|
70
|
+
|
|
71
|
+
traceback.print_exc()
|
|
72
|
+
raise HTTPException(422, f"Could not load image from url: {url}")
|
|
73
|
+
|
|
74
|
+
return preprocess_image(image_pil, convert_to_rgb, fix_orientation)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
|
|
3
|
+
import fal
|
|
4
|
+
from fal.toolkit.image import read_image_from_url
|
|
5
|
+
|
|
6
|
+
from .env import get_requirements
|
|
7
|
+
from .model import get_model
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class NSFWImageDetectionInput(BaseModel):
|
|
11
|
+
image_url: str = Field(
|
|
12
|
+
description="Input image url.",
|
|
13
|
+
examples=[
|
|
14
|
+
"https://storage.googleapis.com/falserverless/model_tests/remove_background/elephant.jpg",
|
|
15
|
+
],
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class NSFWImageDetectionOutput(BaseModel):
|
|
20
|
+
nsfw_probability: float = Field(
|
|
21
|
+
description="The probability of the image being NSFW.",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def check_nsfw_content(pil_image: object):
|
|
26
|
+
import torch
|
|
27
|
+
|
|
28
|
+
model, processor = get_model()
|
|
29
|
+
|
|
30
|
+
with torch.no_grad():
|
|
31
|
+
inputs = processor(images=pil_image, return_tensors="pt")
|
|
32
|
+
outputs = model(**inputs)
|
|
33
|
+
logits = outputs.logits.squeeze() # Remove batch dimension to simplify indexing
|
|
34
|
+
|
|
35
|
+
# Apply softmax to convert logits to probabilities
|
|
36
|
+
probabilities = torch.softmax(logits, dim=0)
|
|
37
|
+
|
|
38
|
+
nsfw_class_index = model.config.label2id.get(
|
|
39
|
+
"nsfw", None
|
|
40
|
+
) # Replace "NSFW" with the exact class name if different
|
|
41
|
+
|
|
42
|
+
# Validate that NSFW class index is found
|
|
43
|
+
if nsfw_class_index is not None:
|
|
44
|
+
nsfw_probability = probabilities[int(nsfw_class_index)].item()
|
|
45
|
+
return nsfw_probability
|
|
46
|
+
else:
|
|
47
|
+
raise ValueError("NSFW class not found in model output.")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def run_nsfw_estimation(
|
|
51
|
+
input: NSFWImageDetectionInput,
|
|
52
|
+
) -> NSFWImageDetectionOutput:
|
|
53
|
+
img = read_image_from_url(input.image_url)
|
|
54
|
+
nsfw_probability = check_nsfw_content(img)
|
|
55
|
+
|
|
56
|
+
return NSFWImageDetectionOutput(nsfw_probability=nsfw_probability)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@fal.function(
|
|
60
|
+
requirements=get_requirements(),
|
|
61
|
+
machine_type="GPU-A6000",
|
|
62
|
+
serve=True,
|
|
63
|
+
)
|
|
64
|
+
def run_nsfw_estimation_on_fal(
|
|
65
|
+
input: NSFWImageDetectionInput,
|
|
66
|
+
) -> NSFWImageDetectionOutput:
|
|
67
|
+
return run_nsfw_estimation(input)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
local = run_nsfw_estimation_on_fal.on(serve=False)
|
|
72
|
+
result = local(
|
|
73
|
+
NSFWImageDetectionInput(
|
|
74
|
+
image_url="https://storage.googleapis.com/falserverless/model_tests/remove_background/elephant.jpg",
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
print(result)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import fal
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@fal.cached
|
|
5
|
+
def get_model():
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
from transformers import AutoModelForImageClassification, ViTImageProcessor
|
|
9
|
+
|
|
10
|
+
os.environ["TRANSFORMERS_CACHE"] = "/data/models"
|
|
11
|
+
os.environ["HF_HOME"] = "/data/models"
|
|
12
|
+
|
|
13
|
+
model = AutoModelForImageClassification.from_pretrained(
|
|
14
|
+
"Falconsai/nsfw_image_detection"
|
|
15
|
+
)
|
|
16
|
+
processor = ViTImageProcessor.from_pretrained("Falconsai/nsfw_image_detection")
|
|
17
|
+
|
|
18
|
+
return model, processor
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
import fal
|
|
4
|
+
|
|
5
|
+
from . import filter_by
|
|
6
|
+
from .nsfw_filter.model import get_model
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@fal.cached
|
|
10
|
+
def load_safety_checker():
|
|
11
|
+
import torch
|
|
12
|
+
from diffusers.pipelines.stable_diffusion.safety_checker import (
|
|
13
|
+
StableDiffusionSafetyChecker,
|
|
14
|
+
)
|
|
15
|
+
from transformers import AutoFeatureExtractor
|
|
16
|
+
|
|
17
|
+
feature_extractor = AutoFeatureExtractor.from_pretrained(
|
|
18
|
+
"CompVis/stable-diffusion-safety-checker",
|
|
19
|
+
torch_dtype="float16",
|
|
20
|
+
)
|
|
21
|
+
safety_checker = StableDiffusionSafetyChecker.from_pretrained(
|
|
22
|
+
"CompVis/stable-diffusion-safety-checker",
|
|
23
|
+
torch_dtype=torch.float16,
|
|
24
|
+
).to("cuda")
|
|
25
|
+
|
|
26
|
+
return feature_extractor, safety_checker
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def run_safety_checker(
|
|
30
|
+
pil_images: list[object],
|
|
31
|
+
) -> list[bool]:
|
|
32
|
+
import numpy as np
|
|
33
|
+
import torch
|
|
34
|
+
|
|
35
|
+
feature_extractor, safety_checker = load_safety_checker()
|
|
36
|
+
|
|
37
|
+
safety_checker_input = feature_extractor(pil_images, return_tensors="pt").to("cuda")
|
|
38
|
+
|
|
39
|
+
np_image = [np.array(val) for val in pil_images]
|
|
40
|
+
|
|
41
|
+
_, has_nsfw_concept = safety_checker(
|
|
42
|
+
images=np_image,
|
|
43
|
+
clip_input=safety_checker_input.pixel_values.to(torch.float16),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return has_nsfw_concept
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def run_safety_checker_v2(pil_images: list, nsfw_threshold: float = 0.5) -> list[bool]:
|
|
50
|
+
import torch
|
|
51
|
+
|
|
52
|
+
model, processor = get_model()
|
|
53
|
+
|
|
54
|
+
has_nsfw_concept = []
|
|
55
|
+
|
|
56
|
+
with torch.no_grad():
|
|
57
|
+
for pil_image in pil_images:
|
|
58
|
+
inputs = processor(
|
|
59
|
+
images=pil_image.convert("RGB"),
|
|
60
|
+
return_tensors="pt",
|
|
61
|
+
)
|
|
62
|
+
outputs = model(**inputs)
|
|
63
|
+
logits = (
|
|
64
|
+
outputs.logits.squeeze()
|
|
65
|
+
) # Remove batch dimension to simplify indexing
|
|
66
|
+
|
|
67
|
+
# Apply softmax to convert logits to probabilities
|
|
68
|
+
probabilities = torch.softmax(logits, dim=0)
|
|
69
|
+
|
|
70
|
+
nsfw_class_index = model.config.label2id.get(
|
|
71
|
+
"nsfw", None
|
|
72
|
+
) # Replace "NSFW" with the exact class name if different
|
|
73
|
+
|
|
74
|
+
# Validate that NSFW class index is found
|
|
75
|
+
if nsfw_class_index is not None:
|
|
76
|
+
nsfw_probability = probabilities[int(nsfw_class_index)].item()
|
|
77
|
+
print("NSFW probability:", nsfw_probability)
|
|
78
|
+
has_nsfw_concept.append(nsfw_probability > nsfw_threshold)
|
|
79
|
+
else:
|
|
80
|
+
raise ValueError("NSFW class not found in model output.")
|
|
81
|
+
|
|
82
|
+
return has_nsfw_concept
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def postprocess_images(
|
|
86
|
+
pil_images: list[object],
|
|
87
|
+
enable_safety_checker: bool = True,
|
|
88
|
+
safety_checker_version: int = 2,
|
|
89
|
+
) -> dict[str, Any]:
|
|
90
|
+
outputs: dict[str, list[Any]] = {
|
|
91
|
+
"images": pil_images,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if enable_safety_checker:
|
|
95
|
+
safety_checker_fn = (
|
|
96
|
+
run_safety_checker_v2 if safety_checker_version == 2 else run_safety_checker
|
|
97
|
+
)
|
|
98
|
+
outputs["has_nsfw_concepts"] = safety_checker_fn(pil_images) # type: ignore
|
|
99
|
+
else:
|
|
100
|
+
outputs["has_nsfw_concepts"] = [False] * len(pil_images)
|
|
101
|
+
|
|
102
|
+
outputs["images"] = filter_by(
|
|
103
|
+
outputs["has_nsfw_concepts"],
|
|
104
|
+
outputs["images"],
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return outputs
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py
RENAMED
|
File without changes
|
{fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py
RENAMED
|
File without changes
|
|
File without changes
|
{fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py
RENAMED
|
File without changes
|
|
File without changes
|
{fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py
RENAMED
|
File without changes
|
|
File without changes
|
{fal-1.2.2 → fal-1.2.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|