notAI-tech / NudeNet

Lightweight nudity detection
https://nudenet.notai.tech/
GNU Affero General Public License v3.0
1.74k stars 339 forks source link

Error when using NudeNet #43

Closed matthewgdv closed 3 years ago

matthewgdv commented 4 years ago

This is the stack trace I'm getting when trying to instanciate a NudeClassifier. A pretty much identical error occurs when trying to instanciate a NudeDetector.

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-9-7465e1bf88d0> in <module>
----> 1 classifier = NudeClassifier()

~\Python\portable\python\Lib\site-packages\nudenet\classifier.py in __init__(self)
    137             pydload.dload(url, save_to_path=model_path, max_time=None)
    138 
--> 139         self.nsfw_model = keras.models.load_model(model_path)
    140 
    141     def classify_video(

~\Python\portable\python\Lib\site-packages\keras\engine\saving.py in load_model(filepath, custom_objects, compile)
    417     f = h5dict(filepath, 'r')
    418     try:
--> 419         model = _deserialize_model(f, custom_objects, compile)
    420     finally:
    421         if opened_new_file:

~\Python\portable\python\Lib\site-packages\keras\engine\saving.py in _deserialize_model(f, custom_objects, compile)
    223         raise ValueError('No model found in config.')
    224     model_config = json.loads(model_config.decode('utf-8'))
--> 225     model = model_from_config(model_config, custom_objects=custom_objects)
    226     model_weights_group = f['model_weights']
    227 

~\Python\portable\python\Lib\site-packages\keras\engine\saving.py in model_from_config(config, custom_objects)
    456                         '`Sequential.from_config(config)`?')
    457     from ..layers import deserialize
--> 458     return deserialize(config, custom_objects=custom_objects)
    459 
    460 

~\Python\portable\python\Lib\site-packages\keras\layers\__init__.py in deserialize(config, custom_objects)
     50     globs['Model'] = models.Model
     51     globs['Sequential'] = models.Sequential
---> 52     return deserialize_keras_object(config,
     53                                     module_objects=globs,
     54                                     custom_objects=custom_objects,

~\Python\portable\python\Lib\site-packages\keras\utils\generic_utils.py in deserialize_keras_object(identifier, module_objects, custom_objects, printable_module_name)
    140             custom_objects = custom_objects or {}
    141             if has_arg(cls.from_config, 'custom_objects'):
--> 142                 return cls.from_config(
    143                     config['config'],
    144                     custom_objects=dict(list(_GLOBAL_CUSTOM_OBJECTS.items()) +

~\Python\portable\python\Lib\site-packages\keras\engine\network.py in from_config(cls, config, custom_objects)
   1020         # First, we create all layers and enqueue nodes to be processed
   1021         for layer_data in config['layers']:
-> 1022             process_layer(layer_data)
   1023         # Then we process nodes in order of layer depth.
   1024         # Nodes that cannot yet be processed (if the inbound node

~\Python\portable\python\Lib\site-packages\keras\engine\network.py in process_layer(layer_data)
   1005             from ..layers import deserialize as deserialize_layer
   1006 
-> 1007             layer = deserialize_layer(layer_data,
   1008                                       custom_objects=custom_objects)
   1009             created_layers[layer_name] = layer

~\Python\portable\python\Lib\site-packages\keras\layers\__init__.py in deserialize(config, custom_objects)
     50     globs['Model'] = models.Model
     51     globs['Sequential'] = models.Sequential
---> 52     return deserialize_keras_object(config,
     53                                     module_objects=globs,
     54                                     custom_objects=custom_objects,

~\Python\portable\python\Lib\site-packages\keras\utils\generic_utils.py in deserialize_keras_object(identifier, module_objects, custom_objects, printable_module_name)
    145                                         list(custom_objects.items())))
    146             with CustomObjectScope(custom_objects):
--> 147                 return cls.from_config(config['config'])
    148         else:
    149             # Then `cls` may be a function returning a class.

~\Python\portable\python\Lib\site-packages\keras\engine\base_layer.py in from_config(cls, config)
   1107             A layer instance.
   1108         """
-> 1109         return cls(**config)
   1110 
   1111     def count_params(self):

~\Python\portable\python\Lib\site-packages\keras\legacy\interfaces.py in wrapper(*args, **kwargs)
     89                 warnings.warn('Update your `' + object_name + '` call to the ' +
     90                               'Keras 2 API: ' + signature, stacklevel=2)
---> 91             return func(*args, **kwargs)
     92         wrapper._original_function = func
     93         return wrapper

~\Python\portable\python\Lib\site-packages\keras\engine\input_layer.py in __init__(self, input_shape, batch_size, batch_input_shape, dtype, input_tensor, sparse, name)
     82         if input_tensor is None:
     83             self.is_placeholder = True
---> 84             input_tensor = K.placeholder(shape=batch_input_shape,
     85                                          dtype=dtype,
     86                                          sparse=self.sparse,

~\Python\portable\python\Lib\site-packages\keras\backend\tensorflow_backend.py in placeholder(shape, ndim, dtype, sparse, name)
    515         x = tf.sparse_placeholder(dtype, shape=shape, name=name)
    516     else:
--> 517         x = tf.placeholder(dtype, shape=shape, name=name)
    518     x._keras_shape = shape
    519     x._uses_learning_phase = False

AttributeError: module 'tensorflow' has no attribute 'placeholder'

I've googled around and these seem to be issues with Keras that have since been fixed? Basically, when I use keras==2.2.4 (which is listed in this library's setup.py) I can't get past this stage.

I've tried upgrading to the latest version of keras for my own curiosity (keras==2.4.3) and the NudeClassifier then works perfectly! However, the NudeDetector now errors like this when I try to instanciate it:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-c0ac40df705b> in <module>
----> 1 detector = NudeDetector()

~\Python\portable\python\Lib\site-packages\nudenet\detector.py in __init__(self, model_name)
     76             pydload.dload(classes_url, save_to_path=classes_path, max_time=None)
     77 
---> 78         self.detection_model = models.load_model(
     79             checkpoint_path, backbone_name="resnet50"
     80         )

~\Python\portable\python\Lib\site-packages\keras_retinanet\models\__init__.py in load_model(filepath, backbone_name)
     81     """
     82     import keras.models
---> 83     return keras.models.load_model(filepath, custom_objects=backbone(backbone_name).custom_objects)
     84 
     85 

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\saving\save.py in load_model(filepath, custom_objects, compile, options)
    180     if (h5py is not None and (
    181         isinstance(filepath, h5py.File) or h5py.is_hdf5(filepath))):
--> 182       return hdf5_format.load_model_from_hdf5(filepath, custom_objects, compile)
    183 
    184     filepath = path_to_string(filepath)

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\saving\hdf5_format.py in load_model_from_hdf5(filepath, custom_objects, compile)
    175       raise ValueError('No model found in config file.')
    176     model_config = json.loads(model_config.decode('utf-8'))
--> 177     model = model_config_lib.model_from_config(model_config,
    178                                                custom_objects=custom_objects)
    179 

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\saving\model_config.py in model_from_config(config, custom_objects)
     53                     '`Sequential.from_config(config)`?')
     54   from tensorflow.python.keras.layers import deserialize  # pylint: disable=g-import-not-at-top
---> 55   return deserialize(config, custom_objects=custom_objects)
     56 
     57 

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\layers\serialization.py in deserialize(config, custom_objects)
    169   """
    170   populate_deserializable_objects()
--> 171   return generic_utils.deserialize_keras_object(
    172       config,
    173       module_objects=LOCAL.ALL_OBJECTS,

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\utils\generic_utils.py in deserialize_keras_object(identifier, module_objects, custom_objects, printable_module_name)
    352 
    353       if 'custom_objects' in arg_spec.args:
--> 354         return cls.from_config(
    355             cls_config,
    356             custom_objects=dict(

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\engine\training.py in from_config(cls, config, custom_objects)
   2236     # be constructed for FunctionalModel
   2237     from tensorflow.python.keras.engine import functional  # pylint: disable=g-import-not-at-top
-> 2238     return functional.Functional.from_config(
   2239         config, custom_objects=custom_objects)
   2240 

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\engine\functional.py in from_config(cls, config, custom_objects)
    614         ValueError: In case of improperly formatted config dict.
    615     """
--> 616     input_tensors, output_tensors, created_layers = reconstruct_from_config(
    617         config, custom_objects)
    618     model = cls(inputs=input_tensors, outputs=output_tensors,

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\engine\functional.py in reconstruct_from_config(config, custom_objects, created_layers)
   1202   # First, we create all layers and enqueue nodes to be processed
   1203   for layer_data in config['layers']:
-> 1204     process_layer(layer_data)
   1205   # Then we process nodes in order of layer depth.
   1206   # Nodes that cannot yet be processed (if the inbound node

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\engine\functional.py in process_layer(layer_data)
   1184       from tensorflow.python.keras.layers import deserialize as deserialize_layer  # pylint: disable=g-import-not-at-top
   1185 
-> 1186       layer = deserialize_layer(layer_data, custom_objects=custom_objects)
   1187       created_layers[layer_name] = layer
   1188 

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\layers\serialization.py in deserialize(config, custom_objects)
    169   """
    170   populate_deserializable_objects()
--> 171   return generic_utils.deserialize_keras_object(
    172       config,
    173       module_objects=LOCAL.ALL_OBJECTS,

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\utils\generic_utils.py in deserialize_keras_object(identifier, module_objects, custom_objects, printable_module_name)
    352 
    353       if 'custom_objects' in arg_spec.args:
--> 354         return cls.from_config(
    355             cls_config,
    356             custom_objects=dict(

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\engine\training.py in from_config(cls, config, custom_objects)
   2236     # be constructed for FunctionalModel
   2237     from tensorflow.python.keras.engine import functional  # pylint: disable=g-import-not-at-top
-> 2238     return functional.Functional.from_config(
   2239         config, custom_objects=custom_objects)
   2240 

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\engine\functional.py in from_config(cls, config, custom_objects)
    614         ValueError: In case of improperly formatted config dict.
    615     """
--> 616     input_tensors, output_tensors, created_layers = reconstruct_from_config(
    617         config, custom_objects)
    618     model = cls(inputs=input_tensors, outputs=output_tensors,

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\engine\functional.py in reconstruct_from_config(config, custom_objects, created_layers)
   1212       if layer in unprocessed_nodes:
   1213         for node_data in unprocessed_nodes.pop(layer):
-> 1214           process_node(layer, node_data)
   1215 
   1216   input_tensors = []

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\engine\functional.py in process_node(layer, node_data)
   1160     if input_tensors is not None:
   1161       input_tensors = base_layer_utils.unnest_if_single_tensor(input_tensors)
-> 1162       output_tensors = layer(input_tensors, **kwargs)
   1163 
   1164       # Update node index map.

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\engine\base_layer.py in __call__(self, *args, **kwargs)
    923     # >> model = tf.keras.Model(inputs, outputs)
    924     if _in_functional_construction_mode(self, inputs, args, kwargs, input_list):
--> 925       return self._functional_construction_call(inputs, args, kwargs,
    926                                                 input_list)
    927 

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\engine\base_layer.py in _functional_construction_call(self, inputs, args, kwargs, input_list)
   1096         # Build layer if applicable (if the `build` method has been
   1097         # overridden).
-> 1098         self._maybe_build(inputs)
   1099         cast_inputs = self._maybe_cast_inputs(inputs, input_list)
   1100 

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\engine\base_layer.py in _maybe_build(self, inputs)
   2641         # operations.
   2642         with tf_utils.maybe_init_scope(self):
-> 2643           self.build(input_shapes)  # pylint:disable=not-callable
   2644       # We must set also ensure that the layer is marked as built, and the build
   2645       # shape is stored since user defined build functions may not be calling

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\layers\convolutional.py in build(self, input_shape)
    204         dtype=self.dtype)
    205     if self.use_bias:
--> 206       self.bias = self.add_weight(
    207           name='bias',
    208           shape=(self.filters,),

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\engine\base_layer.py in add_weight(self, name, shape, dtype, initializer, regularizer, trainable, constraint, partitioner, use_resource, synchronization, aggregation, **kwargs)
    595         caching_device = None
    596 
--> 597     variable = self._add_variable_with_custom_getter(
    598         name=name,
    599         shape=shape,

~\Python\portable\python\Lib\site-packages\tensorflow\python\training\tracking\base.py in _add_variable_with_custom_getter(self, name, shape, dtype, initializer, getter, overwrite, **kwargs_for_getter)
    743         initializer = checkpoint_initializer
    744         shape = None
--> 745     new_variable = getter(
    746         name=name,
    747         shape=shape,

~\Python\portable\python\Lib\site-packages\tensorflow\python\keras\engine\base_layer_utils.py in make_variable(name, shape, dtype, initializer, trainable, caching_device, validate_shape, constraint, use_resource, collections, synchronization, aggregation, partitioner)
    131   # can remove the V1.
    132   variable_shape = tensor_shape.TensorShape(shape)
--> 133   return tf_variables.VariableV1(
    134       initial_value=init_val,
    135       name=name,

~\Python\portable\python\Lib\site-packages\tensorflow\python\ops\variables.py in __call__(cls, *args, **kwargs)
    258   def __call__(cls, *args, **kwargs):
    259     if cls is VariableV1:
--> 260       return cls._variable_v1_call(*args, **kwargs)
    261     elif cls is Variable:
    262       return cls._variable_v2_call(*args, **kwargs)

~\Python\portable\python\Lib\site-packages\tensorflow\python\ops\variables.py in _variable_v1_call(cls, initial_value, trainable, collections, validate_shape, caching_device, name, variable_def, dtype, expected_shape, import_scope, constraint, use_resource, synchronization, aggregation, shape)
    204     if aggregation is None:
    205       aggregation = VariableAggregation.NONE
--> 206     return previous_getter(
    207         initial_value=initial_value,
    208         trainable=trainable,

~\Python\portable\python\Lib\site-packages\tensorflow\python\ops\variables.py in <lambda>(**kwargs)
    197                         shape=None):
    198     """Call on Variable class. Useful to force the signature."""
--> 199     previous_getter = lambda **kwargs: default_variable_creator(None, **kwargs)
    200     for _, getter in ops.get_default_graph()._variable_creator_stack:  # pylint: disable=protected-access
    201       previous_getter = _make_getter(getter, previous_getter)

~\Python\portable\python\Lib\site-packages\tensorflow\python\ops\variable_scope.py in default_variable_creator(next_creator, **kwargs)
   2581   if use_resource:
   2582     distribute_strategy = kwargs.get("distribute_strategy", None)
-> 2583     return resource_variable_ops.ResourceVariable(
   2584         initial_value=initial_value,
   2585         trainable=trainable,

~\Python\portable\python\Lib\site-packages\tensorflow\python\ops\variables.py in __call__(cls, *args, **kwargs)
    262       return cls._variable_v2_call(*args, **kwargs)
    263     else:
--> 264       return super(VariableMetaclass, cls).__call__(*args, **kwargs)
    265 
    266 

~\Python\portable\python\Lib\site-packages\tensorflow\python\ops\resource_variable_ops.py in __init__(self, initial_value, trainable, collections, validate_shape, caching_device, name, dtype, variable_def, import_scope, constraint, distribute_strategy, synchronization, aggregation, shape)
   1505       self._init_from_proto(variable_def, import_scope=import_scope)
   1506     else:
-> 1507       self._init_from_args(
   1508           initial_value=initial_value,
   1509           trainable=trainable,

~\Python\portable\python\Lib\site-packages\tensorflow\python\ops\resource_variable_ops.py in _init_from_args(self, initial_value, trainable, collections, caching_device, name, dtype, constraint, synchronization, aggregation, distribute_strategy, shape)
   1649           with ops.name_scope("Initializer"), device_context_manager(None):
   1650             initial_value = ops.convert_to_tensor(
-> 1651                 initial_value() if init_from_fn else initial_value,
   1652                 name="initial_value", dtype=dtype)
   1653           if shape is not None:

~\Python\portable\python\Lib\site-packages\keras_retinanet\initializers.py in __call__(self, shape, dtype)
     35     def __call__(self, shape, dtype=None):
     36         # set bias to -log((1 - p)/p) for foreground
---> 37         result = np.ones(shape, dtype=dtype) * -math.log((1 - self.probability) / self.probability)
     38 
     39         return result

~\Python\portable\python\Lib\site-packages\numpy\core\numeric.py in ones(shape, dtype, order)
    205 
    206     """
--> 207     a = empty(shape, dtype, order)
    208     multiarray.copyto(a, 1, casting='unsafe')
    209     return a

TypeError: data type not understood

Do you know why this is happening? I'd love to play around with this library but I can't figure out how to get past these errors.

If it helps, I'm running Python 3.8.2

Thanks in advance!

bedapudi6788 commented 4 years ago

I would suggest using python 3.6.x and keras==2.2.4, tensorflow==1.14

These are the versions i tested the model with.

matthewgdv commented 4 years ago

Heya @bedapudi6788, thanks for your help!

I've just tried downgrading my version of tensorflow from 2.3.0 to 1.14 as you've suggested, and I'm getting this error:

C:\Users\matthewgdv>pip install tensorflow==1.14 --upgrade
ERROR: Could not find a version that satisfies the requirement tensorflow==1.14 (from versions: 2.2.0rc1, 2.2.0rc2, 2.2.0rc3, 2.2.0rc4, 2.2.0, 2.3.0rc0, 2.3.0rc1, 2.3.0rc2, 2.3.0)
ERROR: No matching distribution found for tensorflow==1.14

Is there any chance the tensorflow team has removed that version? If so, how would you recommend setting up a working environment for NudeNet?

I'm sorry. I realize environment issues are literally the worst and just the most boring and obnoxious thing ever. :(

bedapudi6788 commented 4 years ago

I think that's because of the python version being higher.

https://github.com/notAI-tech/LogoDet/blob/master/requirements.txt

These versions might work for your python version.

wget https://raw.githubusercontent.com/notAI-tech/LogoDet/master/requirements.txt
pip install -r requirements.txt
matthewgdv commented 4 years ago

With those versions the NudeClassifier is now working, but the NudeDetector is now erroring like so:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-13-c0ac40df705b> in <module>
----> 1 detector = NudeDetector()

~\Python\portable\python\Lib\site-packages\nudenet\detector.py in __init__(self, model_name)
     76             pydload.dload(classes_url, save_to_path=classes_path, max_time=None)
     77 
---> 78         self.detection_model = models.load_model(
     79             checkpoint_path, backbone_name="resnet50"
     80         )

~\Python\portable\python\Lib\site-packages\keras_retinanet\models\__init__.py in load_model(filepath, backbone_name)
     81     """
     82     import keras.models
---> 83     return keras.models.load_model(filepath, custom_objects=backbone(backbone_name).custom_objects)
     84 
     85 

~\Python\portable\python\Lib\site-packages\keras\engine\saving.py in load_wrapper(*args, **kwargs)
    490                 os.remove(tmp_filepath)
    491             return res
--> 492         return load_function(*args, **kwargs)
    493 
    494     return load_wrapper

~\Python\portable\python\Lib\site-packages\keras\engine\saving.py in load_model(filepath, custom_objects, compile)
    582     if H5Dict.is_supported_type(filepath):
    583         with H5Dict(filepath, mode='r') as h5dict:
--> 584             model = _deserialize_model(h5dict, custom_objects, compile)
    585     elif hasattr(filepath, 'write') and callable(filepath.write):
    586         def load_function(h5file):

~\Python\portable\python\Lib\site-packages\keras\engine\saving.py in _deserialize_model(h5dict, custom_objects, compile)
    272         raise ValueError('No model found in config.')
    273     model_config = json.loads(model_config.decode('utf-8'))
--> 274     model = model_from_config(model_config, custom_objects=custom_objects)
    275     model_weights_group = h5dict['model_weights']
    276 

~\Python\portable\python\Lib\site-packages\keras\engine\saving.py in model_from_config(config, custom_objects)
    625                         '`Sequential.from_config(config)`?')
    626     from ..layers import deserialize
--> 627     return deserialize(config, custom_objects=custom_objects)
    628 
    629 

~\Python\portable\python\Lib\site-packages\keras\layers\__init__.py in deserialize(config, custom_objects)
    163     globs['Model'] = models.Model
    164     globs['Sequential'] = models.Sequential
--> 165     return deserialize_keras_object(config,
    166                                     module_objects=globs,
    167                                     custom_objects=custom_objects,

~\Python\portable\python\Lib\site-packages\keras\utils\generic_utils.py in deserialize_keras_object(identifier, module_objects, custom_objects, printable_module_name)
    142             custom_objects = custom_objects or {}
    143             if has_arg(cls.from_config, 'custom_objects'):
--> 144                 return cls.from_config(
    145                     config['config'],
    146                     custom_objects=dict(list(_GLOBAL_CUSTOM_OBJECTS.items()) +

~\Python\portable\python\Lib\site-packages\keras\engine\network.py in from_config(cls, config, custom_objects)
   1073                         node_data = node_data_list[node_index]
   1074                         try:
-> 1075                             process_node(layer, node_data)
   1076 
   1077                         # If the node does not have all inbound layers

~\Python\portable\python\Lib\site-packages\keras\engine\network.py in process_node(layer, node_data)
   1023             # and building the layer if needed.
   1024             if input_tensors:
-> 1025                 layer(unpack_singleton(input_tensors), **kwargs)
   1026 
   1027         def process_layer(layer_data):

~\Python\portable\python\Lib\site-packages\keras\backend\tensorflow_backend.py in symbolic_fn_wrapper(*args, **kwargs)
     73         if _SYMBOLIC_SCOPE.value:
     74             with get_graph().as_default():
---> 75                 return func(*args, **kwargs)
     76         else:
     77             return func(*args, **kwargs)

~\Python\portable\python\Lib\site-packages\keras\engine\base_layer.py in __call__(self, inputs, **kwargs)
    487             # Actually call the layer,
    488             # collecting output(s), mask(s), and shape(s).
--> 489             output = self.call(inputs, **kwargs)
    490             output_mask = self.compute_mask(inputs, previous_mask)
    491 

~\Python\portable\python\Lib\site-packages\keras_retinanet\layers\_misc.py in call(self, inputs, **kwargs)
    107             return output
    108         else:
--> 109             return backend.resize_images(source, (target_shape[1], target_shape[2]), method='nearest')
    110 
    111     def compute_output_shape(self, input_shape):

~\Python\portable\python\Lib\site-packages\keras_retinanet\backend\tensorflow_backend.py in resize_images(images, size, method, align_corners)
     66         'area'    : tensorflow.image.ResizeMethod.AREA,
     67     }
---> 68     return tensorflow.image.resize_images(images, size, methods[method], align_corners)
     69 
     70 

AttributeError: module 'tensorflow._api.v2.image' has no attribute 'resize_images'

I'll try and set up a Python 3.6 environment tomorrow to test it out. That said, I do suspect that it will work and that it's down to spooky interactions between the Keras/Tensorflow dependencies on newer versions of Python.

It's your call of course whether you want to support newer versions of the language or not, but I'd be really grateful if you did. I pretty much religiously upgrade my environments to the newest interpreter version usually within a month of release (unless some core dependencies I use break and need more time to catch up), and I dislike spinning up virtualenvs on older versions, since I hate having to consciously censor my code and avoid using awesome new language features (the most recent awesome one was assignment expressions <3 ) just to accomodate a dependancy.

Granted, not everyone is like me, and you for sure don't have to cater to us, but it would be awesome if you did streamline the process of using NudeNet with an up-to-date interpreter. I think over time it might also give the project more longevity.

Anyways, thanks again!

bedapudi6788 commented 4 years ago

with https://github.com/notAI-tech/LogoDet/blob/master/requirements.txt these requirements, detector should have worked.

if you don't mind, can you do the following and check once

pip uninstall keras-retinanet
wget https://raw.githubusercontent.com/notAI-tech/LogoDet/master/requirements.txt
pip install -r requirements.txt
Adjenz commented 4 years ago

with https://github.com/notAI-tech/LogoDet/blob/master/requirements.txt these requirements, detector should have worked.

if you don't mind, can you do the following and check once

pip uninstall keras-retinanet
wget https://raw.githubusercontent.com/notAI-tech/LogoDet/master/requirements.txt
pip install -r requirements.txt

Hi mate, I have that issue too.

I installed all the requirements with the good versions. This is what it shows :

>>> from nudenet import NudeClassifier Using TensorFlow backend. c>>> classifier = NudeClassifier() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/adrien/.local/lib/python3.8/site-packages/nudenet/classifier.py", line 139, in __init__ self.nsfw_model = keras.models.load_model(model_path) File "/home/adrien/.local/lib/python3.8/site-packages/keras/engine/saving.py", line 419, in load_model model = _deserialize_model(f, custom_objects, compile) File "/home/adrien/.local/lib/python3.8/site-packages/keras/engine/saving.py", line 225, in _deserialize_model model = model_from_config(model_config, custom_objects=custom_objects) File "/home/adrien/.local/lib/python3.8/site-packages/keras/engine/saving.py", line 458, in model_from_config return deserialize(config, custom_objects=custom_objects) File "/home/adrien/.local/lib/python3.8/site-packages/keras/layers/__init__.py", line 52, in deserialize return deserialize_keras_object(config, File "/home/adrien/.local/lib/python3.8/site-packages/keras/utils/generic_utils.py", line 142, in deserialize_keras_object return cls.from_config( File "/home/adrien/.local/lib/python3.8/site-packages/keras/engine/network.py", line 1022, in from_config process_layer(layer_data) File "/home/adrien/.local/lib/python3.8/site-packages/keras/engine/network.py", line 1007, in process_layer layer = deserialize_layer(layer_data, File "/home/adrien/.local/lib/python3.8/site-packages/keras/layers/__init__.py", line 52, in deserialize return deserialize_keras_object(config, File "/home/adrien/.local/lib/python3.8/site-packages/keras/utils/generic_utils.py", line 147, in deserialize_keras_object return cls.from_config(config['config']) File "/home/adrien/.local/lib/python3.8/site-packages/keras/engine/base_layer.py", line 1109, in from_config return cls(**config) File "/home/adrien/.local/lib/python3.8/site-packages/keras/legacy/interfaces.py", line 91, in wrapper return func(*args, **kwargs) File "/home/adrien/.local/lib/python3.8/site-packages/keras/engine/input_layer.py", line 84, in __init__ input_tensor = K.placeholder(shape=batch_input_shape, File "/home/adrien/.local/lib/python3.8/site-packages/keras/backend/tensorflow_backend.py", line 517, in placeholder x = tf.placeholder(dtype, shape=shape, name=name) AttributeError: module 'tensorflow' has no attribute 'placeholder'

Adjenz commented 4 years ago

Same thing on Google Colab :

`AttributeError Traceback (most recent call last)

in () ----> 1 classifier = NudeClassifier() 13 frames /usr/local/lib/python3.6/dist-packages/keras/backend/tensorflow_backend.py in placeholder(shape, ndim, dtype, sparse, name) 515 x = tf.sparse_placeholder(dtype, shape=shape, name=name) 516 else: --> 517 x = tf.placeholder(dtype, shape=shape, name=name) 518 x._keras_shape = shape 519 x._uses_learning_phase = False AttributeError: module 'tensorflow' has no attribute 'placeholder'`
bedapudi6788 commented 4 years ago

Sorry, at the moment you will have to use it in a venv with tf 1.14 and keras 2.24. Or, using it as rest API via docker is a good idea if you don't want to create new venv.

I will try to support newer versions in future.

Adjenz commented 4 years ago

Sorry, at the moment you will have to use it in a venv with tf 1.14 and keras 2.24. Or, using it as rest API via docker is a good idea if you don't want to create new venv.

I will try to support newer versions in future.

Ok then I'll just learn how to use Docker. Thanks mate.

entrpn commented 4 years ago

You can use this with tf2 but you need to clone the project and in classifier.py you need to add to the top of the file

import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()

And then instead of using keras, you import tf.keras.

Full file change:

import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()

import os
import cv2
#import keras
import pydload
import logging
import numpy as np
from .video_utils import get_interest_frames_from_video

from PIL import Image as pil_image

if pil_image is not None:
    _PIL_INTERPOLATION_METHODS = {
        "nearest": pil_image.NEAREST,
        "bilinear": pil_image.BILINEAR,
        "bicubic": pil_image.BICUBIC,
    }
    # These methods were only introduced in version 3.4.0 (2016).
    if hasattr(pil_image, "HAMMING"):
        _PIL_INTERPOLATION_METHODS["hamming"] = pil_image.HAMMING
    if hasattr(pil_image, "BOX"):
        _PIL_INTERPOLATION_METHODS["box"] = pil_image.BOX
    # This method is new in version 1.1.3 (2013).
    if hasattr(pil_image, "LANCZOS"):
        _PIL_INTERPOLATION_METHODS["lanczos"] = pil_image.LANCZOS

def load_img(
    path, grayscale=False, color_mode="rgb", target_size=None, interpolation="nearest"
):
    """Loads an image into PIL format.

    :param path: Path to image file.
    :param grayscale: DEPRECATED use `color_mode="grayscale"`.
    :param color_mode: One of "grayscale", "rgb", "rgba". Default: "rgb".
        The desired image format.
    :param target_size: Either `None` (default to original size)
        or tuple of ints `(img_height, img_width)`.
    :param interpolation: Interpolation method used to resample the image if the
        target size is different from that of the loaded image.
        Supported methods are "nearest", "bilinear", and "bicubic".
        If PIL version 1.1.3 or newer is installed, "lanczos" is also
        supported. If PIL version 3.4.0 or newer is installed, "box" and
        "hamming" are also supported. By default, "nearest" is used.

    :return: A PIL Image instance.
    """
    if grayscale is True:
        logging.warn("grayscale is deprecated. Please use " 'color_mode = "grayscale"')
        color_mode = "grayscale"
    if pil_image is None:
        raise ImportError(
            "Could not import PIL.Image. " "The use of `load_img` requires PIL."
        )

    if isinstance(path, type("")):
        img = pil_image.open(path)
    else:
        path = cv2.cvtColor(path, cv2.COLOR_BGR2RGB)
        img = pil_image.fromarray(path)

    if color_mode == "grayscale":
        if img.mode != "L":
            img = img.convert("L")
    elif color_mode == "rgba":
        if img.mode != "RGBA":
            img = img.convert("RGBA")
    elif color_mode == "rgb":
        if img.mode != "RGB":
            img = img.convert("RGB")
    else:
        raise ValueError('color_mode must be "grayscale", "rgb", or "rgba"')
    if target_size is not None:
        width_height_tuple = (target_size[1], target_size[0])
        if img.size != width_height_tuple:
            if interpolation not in _PIL_INTERPOLATION_METHODS:
                raise ValueError(
                    "Invalid interpolation method {} specified. Supported "
                    "methods are {}".format(
                        interpolation, ", ".join(_PIL_INTERPOLATION_METHODS.keys())
                    )
                )
            resample = _PIL_INTERPOLATION_METHODS[interpolation]
            img = img.resize(width_height_tuple, resample)
    return img

def load_images(image_paths, image_size, image_names):
    """
    Function for loading images into numpy arrays for passing to model.predict
    inputs:
        image_paths: list of image paths to load
        image_size: size into which images should be resized

    outputs:
        loaded_images: loaded images on which keras model can run predictions
        loaded_image_indexes: paths of images which the function is able to process

    """
    loaded_images = []
    loaded_image_paths = []

    for i, img_path in enumerate(image_paths):
        try:
            image = load_img(img_path, target_size=image_size)
            image = tf.keras.preprocessing.image.img_to_array(image)
            image /= 255
            loaded_images.append(image)
            loaded_image_paths.append(image_names[i])
        except Exception as ex:
            logging.exception(f"Error reading {img_path} {ex}", exc_info=True)

    return np.asarray(loaded_images), loaded_image_paths

class Classifier:
    """
        Class for loading model and running predictions.
        For example on how to use take a look the if __name__ == '__main__' part.
    """

    nsfw_model = None

    def __init__(self):
        """
            model = Classifier()
        """
        url = "https://github.com/bedapudi6788/NudeNet/releases/download/v0/classifier_model"
        home = os.path.expanduser("~")
        model_folder = os.path.join(home, ".NudeNet/")
        if not os.path.exists(model_folder):
            os.mkdir(model_folder)

        model_path = os.path.join(model_folder, "classifier")

        if not os.path.exists(model_path):
            print("Downloading the checkpoint to", model_path)
            pydload.dload(url, save_to_path=model_path, max_time=None)

        self.nsfw_model = tf.keras.models.load_model(model_path)

    def classify_video(
        self,
        video_path,
        batch_size=4,
        image_size=(256, 256),
        categories=["unsafe", "safe"],
    ):
        frame_indices = None
        frame_indices, frames, fps, video_length = get_interest_frames_from_video(
            video_path
        )
        logging.debug(
            f"VIDEO_PATH: {video_path}, FPS: {fps}, Important frame indices: {frame_indices}, Video length: {video_length}"
        )

        frames, frame_names = load_images(frames, image_size, image_names=frame_indices)

        if not frame_names:
            return {}

        model_preds = self.nsfw_model.predict(frames, batch_size=batch_size)
        preds = np.argsort(model_preds, axis=1).tolist()

        probs = []
        for i, single_preds in enumerate(preds):
            single_probs = []
            for j, pred in enumerate(single_preds):
                single_probs.append(model_preds[i][pred])
                preds[i][j] = categories[pred]

            probs.append(single_probs)

        return_preds = {
            "metadata": {
                "fps": fps,
                "video_length": video_length,
                "video_path": video_path,
            },
            "preds": {},
        }

        for i, frame_name in enumerate(frame_names):
            return_preds["preds"][frame_name] = {}
            for _ in range(len(preds[i])):
                return_preds["preds"][frame_name][preds[i][_]] = probs[i][_]

        return return_preds

    def classify(
        self,
        image_paths=[],
        batch_size=4,
        image_size=(256, 256),
        categories=["unsafe", "safe"],
    ):
        """
            inputs:
                image_paths: list of image paths or can be a string too (for single image)
                batch_size: batch_size for running predictions
                image_size: size to which the image needs to be resized
                categories: since the model predicts numbers, categories is the list of actual names of categories
        """
        if isinstance(image_paths, str):
            image_paths = [image_paths]

        loaded_images, loaded_image_paths = load_images(
            image_paths, image_size, image_names=image_paths
        )

        if not loaded_image_paths:
            return {}

        model_preds = self.nsfw_model.predict(
            loaded_images, batch_size=batch_size
        )

        preds = np.argsort(model_preds, axis=1).tolist()

        probs = []
        for i, single_preds in enumerate(preds):
            single_probs = []
            for j, pred in enumerate(single_preds):
                single_probs.append(model_preds[i][pred])
                preds[i][j] = categories[pred]

            probs.append(single_probs)

        images_preds = {}

        for i, loaded_image_path in enumerate(loaded_image_paths):
            if not isinstance(loaded_image_path, str):
                loaded_image_path = i

            images_preds[loaded_image_path] = {}
            for _ in range(len(preds[i])):
                images_preds[loaded_image_path][preds[i][_]] = probs[i][_]

        return images_preds

if __name__ == "__main__":
    print(
        '\n Enter path for the keras weights, leave empty to use "./nsfw.299x299.h5" \n'
    )
    weights_path = input().strip()
    if not weights_path:
        weights_path = "../nsfw.299x299.h5"

    m = Classifier()

    while 1:
        print(
            "\n Enter single image path or multiple images seperated by || (2 pipes) \n"
        )
        images = input().split("||")
        images = [image.strip() for image in images]
        print(m.predict(images), "\n")

Btw, thank you for this repo. Saved me so much time.

bedapudi6788 commented 3 years ago

@matthewgdv @Screamus57 @entrpn from the current release, nudenet doesn't require any version of tensorflow and will work as expected on all newer python versions.