From watchdog.observers import Observer
From watchdog.events Import *
Import time
Import Sys
Import OS
Import _io
From collections Import Namedtuple
From PIL import Image
Import Argparse
Class Nude (object):
Skin = Namedtuple ("Skin", "id skin region x y")
def __init__ (self, path_or_image):
# If the path_or_image is an instance of the Image.image type, assign the value directly
If Isinstance (Path_or_image, image.image):
Self.image = Path_or_image
# If Path_or_image is an instance of STR type, open the picture
Elif isinstance (Path_or_image, str):
Self.image = Image.open (path_or_image)
# Get pictures of all color channels
Bands = Self.image.getbands ()
# to determine whether a single-channel picture (also known as a grayscale), is to convert the grayscale image to an RGB graph
If Len (bands) = = 1:
# Create a new RGB image of the same size
new_img = Image.new ("RGB", Self.image.size)
# Copy Grayscale image self.image to RGB figure New_img.paste (PIL automatically color channel conversion)
New_img.paste (Self.image)
f = self.image.filename
# Replace Self.image
Self.image = new_img
Self.image.filename = f
# Store all Skin objects for all pixels in the image
Self.skin_map = []
# The skin area is detected, the index of the element is the skin area number, and the element is a list containing some skin objects
Self.detected_regions = []
# elements are lists that contain some int objects (area numbers)
# The regions in these elements represent areas that are to be merged
Self.merge_regions = []
# The integrated skin area, the index of the element is the skin area number, the element is a list containing some skin objects
Self.skin_regions = []
# The region number of the recently merged two skin regions, initialized to-1
Self.last_from, self.last_to =-1,-1
# Erotic Image Judging Results
Self.result = None
# Process the information you get
Self.message = None
# Image Width High
Self.width, self.height = self.image.size
# Total Image pixels
Self.total_pixels = Self.width * self.height
def resize (self, maxwidth=1000, maxheight=1000):
"""
Resize the picture proportionally based on the maximum width and height,
Note: This may affect the results of the detection algorithm
Returns 0 if there is no change
Original width greater than maxwidth return 1
Original height greater than maxheight return 2
The original width height is greater than MaxWidth, MaxHeight returns 3
MaxWidth-Maximum picture width
MaxHeight-picture max height
can be set to False to ignore
"" "
# Store return value
Ret = 0
If MAXWIDTH:
if self.width > maxwidth:
wpercent = (maxwidth/self.width)
hsize = Int ((self.height * wpercent))
FName = Self.image.filename
# Image.lanczos is a resampling filter for antialiasing
Self.image = Self.image.resize ((maxwidth, hsize), Image.lanczos)
Self.image.filename = fname
Self.width, self.height = self.image.size
Self.total_pixels = Self.width * Self.height
ret + = 1
If maxheight:
If Self.height > maxheight:
hpercent = (Maxheight/flo at (self.height))
wsize = Int ((float (self.width) * FLOAT (hpercent)))
FName = self.image.filename
Self.image = Self.image.resize ((wsize, maxheight), image.lanczos)
Self.image.filename = fname
Self.width, self.height = Self.image.size
Self.total_pixels = self.width * Self.height
ret + = 2
return ret
# analysis function
Def parse (self):
# If there is a result, return this object
If Self.result is not None:
return self
# Get image all pixel data
pixels = Self.image.load ()
# Traverse each pixel
For y in range (self.height):
For x in range (self.width):
# Gets the RGB three-channel value of the pixel
# [x, Y] is an easy way to do [(y)]
r = pixels[x, Y][0] # red
g = Pixels[x, y][1] # Green
b = Pixels[x, y][2] # Blue
# Determine if the current pixel is a skin color pixel
Isskin = True If Self._classify_skin (R, G, b) Else False
# Assign a unique ID value to each pixel (1, 2, 3...height*width)
# Note X, Y's value starts from zero
_id = x + y * self.width + 1
# Create a corresponding Skin object for each pixel and add it to the Self.skin_map
Self.skin_map.append (self. Skin (_id, Isskin, None, X, y))
# skip This loop if the current pixel is not a skin-color pixel
If not Isskin:
Continue
# set the upper left corner as the origin, the neighboring pixels are the symbol *, the current pixel is the symbol ^, then the relationship between the positions is usually
# ***
# *^
# There is a list of adjacent pixel indexes, which are stored in order from large to small, and the order changes have influence
# Note that _id is starting at 1, and the corresponding index is _id-1
Check_indexes = [_id-2, # pixels to the left of the current pixel
_id-self.width-2, # pixels at the top left of the current pixel
_id-self.width-1, # pixels above the current pixel
_id-self.width] # pixels at the top right of the current pixel
# used to record the area number of the skin color pixels in the neighboring pixel, initialized to-1
Region =-1
# Iterate through the index of each neighboring pixel
For index in check_indexes:
# try to index the Skin object of neighboring pixels, no then jump out of the loop
Try
Self.skin_map[index]
Except Indexerror:
Break
# Neighboring pixels are skin-color pixels:
If Self.skin_map[index].skin:
# If the neighboring pixel and region of the current pixel are valid values, and they are different, and the same merge task has not been added
if (self.skin_map[index].region! = None and
Region! = None and Region! =-1 and
Self.skin_map[index].region! = region and
Self.last_from! = region and
Self.last_to! = self.skin_map[index].region):
# So this adds merge tasks for both regions
Self._add_merge (region, Self.skin_map[index].region)
# Record the region number of this neighboring pixel
Region = Self.skin_map[index].region
# after traversing all neighboring pixels, if region is still equal to-1, it means that all neighboring pixels are not skin-color pixels
If region = =-1:
# Change the property to the new zone number, note that the tuple is immutable and cannot be changed directly
_skin = Self.skin_map[_id-1]._replace (Region=len (self.detected_regions))
SELF.SKIN_MAP[_ID-1] = _skin
# Create this skin color pixel area as a new region
Self.detected_regions.append ([self.skin_map[_id-1]])
# Region is not equal to 1 and not equal to none, indicating that there is an area number as a valid value for the neighboring skin pixels
Elif Region! = None:
# Change the region number of this pixel to be the same as neighboring pixels
_skin = Self.skin_map[_id-1]._replace (region=region)
SELF.SKIN_MAP[_ID-1] = _skin
# Add this pixel to the list of pixels in this region
Self.detected_regions[region].append (Self.skin_map[_id-1])
# Complete All Area merge tasks, merge the sorted areas into self.skin_regions
Self._merge (Self.detected_regions, self.merge_regions)
# Analyze skin area to get judgment result
Self._analyse_regions ()
return self
# The elements of self.merge_regions are lists that contain some int objects (area numbers)
# The area represented by the Self.merge_regions element is the area to be merged
# This method adds two regions to be merged into the Self.merge_regions
def _add_merge (self, _from, _to):
# Two zone numbers assigned to class properties
Self.last_from = _from
Self.last_to = _to
# record an index value of self.merge_regions, initialized to-1
From_index =-1
# record an index value of self.merge_regions, initialized to-1
To_index =-1
# Iterate through the elements of each self.merge_regions
For index, region in enumerate (self.merge_regions):
# iterate through each area number in the element
For R_index Inch:
if R_index = = _from:
From_index = Index
if R_index = = _to:
To_index = Index
# If two region numbers are present in self.merge_regions
If From_index! =-1 and To_index! =-1:
# If these two zones exist in two lists, respectively
# then merge the two lists
If From_index! = To_index:
Self.merge_regions[from_index].extend (Self.merge_regions[to_index])
Del (Self.merge_regions[to_index])
Return
# If two zone numbers are not present in self.merge_regions
if From_index = =-1 and To_index = =-1:
# Create a new zone number list
Self.merge_regions.append ([_from, _to])
Return
# If one of the two region numbers is present in self.merge_regions
If From_index! =-1 and To_index = =-1:
# will not exist in the Self.merge_regions area number
# Add to the list where another zone number is located
Self.merge_regions[from_index].append (_to)
Return
# If one of the two area numbers to be merged exists in Self.merge_regions
if From_index = =-1 and To_index! =-1:
# will not exist in the Self.merge_regions area number
# Add to the list where another zone number is located
Self.merge_regions[to_index].append (_from)
Return
# Merge the skin area of the merge
def _merge (self, detected_regions, merge_regions):
# New List New_detected_regions
# its elements will be a list of Skin objects that contain some pixels
# The elements of New_detected_regions represent the skin area, and the element index is the area number
New_detected_regions = []
# Merges all regions represented by the region number in the element in Merge_regions
For index, region in enumerate (merge_regions):
Try
New_detected_regions[index]
Except Indexerror:
New_detected_regions.append ([])
For R_index Inch:
New_detected_regions[index].extend (Detected_regions[r_index])
Detected_regions[r_index] = []
# Add the rest of the remaining skin areas to the new_detected_regions
For region in Detected_regions:
If Len (region) > 0:
New_detected_regions.append (region)
# Clean New_detected_regions
Self._clear_regions (new_detected_regions)
# Skin Area cleanup function
# save only a few pixels greater than the specified number of skin areas
def _clear_regions (self, detected_regions):
For region in Detected_regions:
If Len (region) > 30:
Self.skin_regions.append (region)
# Analysis Area
def _analyse_regions (self):
# If the skin area is less than 3, not a pornographic
# If Len (self.skin_regions) < 3:
# self.message = "Less than 2 skins regions ({_skin_regions_size})". Format (
# _skin_regions_size=len (self.skin_regions))
# Self.result = False
# return Self.result
# Sort the Skin area
Self.skin_regions = sorted (self.skin_regions, Key=lambda S:len (s),
Reverse=true)
# Calculate total number of skin pixels
Total_skin = float (SUM ([Len (skin_region) for skin_region in Self.skin_regions])
# If the ratio of the skin area to the entire image is less than 15%, then not pornographic pictures
If Total_skin/self.total_pixels * < 15:
Self.message = "Total skin percentage lower than ({:. 2f})". Format (total_skin/self.total_pixels * 100)
Self.result = False
Return Self.result
# If the maximum skin area is less than 45% of total skin area, not pornographic pictures
If Len (Self.skin_regions[0])/Total_skin * < 45:
Self.message = "The biggest region contains less than ({:. 2f})". Format (len (self.skin_regions[0])/total_skin * 100)
Self.result = False
Return Self.result
# More than 60 skin areas, not pornographic images
If Len (self.skin_regions) > 60:
Self.message = "More than skin regions ({})". Format (len (self.skin_regions))
Self.result = False
Return Self.result
# Other situations for erotic pictures
Self.message = "nude!!"
Self.result = True
Return Self.result
# pixel-based skin tone detection technology
def _classify_skin (self, r, G, b):
# Judging by RGB values
Rgb_classifier = r >
G > G < and \
b > and \
Max ([R, G, b])-min ([R, G, b]) >
ABS (R-G) >
R > G and \
R > B
# based on the processed RGB value determination
NR, ng, NB = self._to_normalized (R, G, b)
Norm_rgb_classifier = nr/ng > 1.185 and \
Float (R * b)/((R + G + B) * * 2) > 0.107 and \
Float (R * g)/((R + G + B) * * 2) > 0.112
# Judging in HSV color mode
H, s, v = SELF._TO_HSV (R, G, b)
Hsv_classifier = h > 0 and \
H < and \
s > 0.23 and \
s < 0.68
# YCbCr in color mode
Y, cb, CR = SELF._TO_YCBCR (R, G, b)
Ycbcr_classifier = 97.5 <= cb <= 142.5 and 134 <= CR <= 176
# The effect is not very good, also need to change the formula
# return Rgb_classifier or Norm_rgb_classifier or Hsv_classifier or ycbcr_classifier
Return Ycbcr_classifier
def _to_normalized (self, r, G, b):
If r = = 0:
r = 0.0001
If G = = 0:
g = 0.0001
If b = = 0:
b = 0.0001
_sum = Float (r + G + B)
return [R/_sum, G/_sum, b/_sum]
def _to_ycbcr (self, r, G, b):
# Source of the formula:
# Http://stackoverflow.com/questions/19459831/rgb-to-ycbcr-conversion-problems
y =. 299*r +. 587*g +. 114*b
CB = 128-0.168736*r-0.331364*g + 0.5*b
CR = + 0.5*r-0.418688*g-0.081312*b
Return y, CB, CR
def _TO_HSV (self, r, G, b):
H = 0
_sum = Float (r + G + B)
_max = float (max ([R, G, b]))
_min = float (min ([R, G, b]))
diff = Float (_max-_min)
If _sum = = 0:
_sum = 0.0001
If _max = = r:
if diff = = 0:
H = sys.maxsize
Else
h = (g-b)/diff
elif _max = = G:
H = 2 + ((g-r)/diff)
Else
H = 4 + ((r-g)/diff)
H *= 60
If h < 0:
H + = 360
return [H, 1.0-(3.0 * (_min/_sum)), (1.0/3.0) * _max]
def inspect (self):
_image = ' {} {} {}x{} '. Format (Self.image.filename, Self.image.format, Self.width, Self.height)
Return "{_image}: Result={_result} message= ' {_message} '". Format (_image=_image, _result=self.result, _message= Self.message)
# A picture file will be generated in the source file directory to visualize the skin area
def showskinregions (self):
# method returns when no results are obtained
If Self.result is None:
Return
# A collection of skin pixel IDs
Skinidset = set ()
# Make a copy of the original
Simage = Self.image
# Loading Data
Simagedata = Simage.load ()
# Put the ID of the skin pixel into skinidset
For SR in Self.skin_regions:
For Pixel in SR:
Skinidset.add (pixel.id)
# Set the skin pixels in the image to white and the rest to black
For Pixel in Self.skin_map:
If Pixel.id not in Skinidset:
Simagedata[pixel.x, Pixel.y] = 0, 0, 0
Else
Simagedata[pixel.x, pixel.y] = 255, 255, 255
# source File Absolute Path
FilePath = Os.path.abspath (self.image.filename)
# directory where source files are located
FileDirectory = Os.path.dirname (filePath) + '/'
# The full filename of the source file
Filefullname = Os.path.basename (FilePath)
# separate the full file name of the source file to get the file name and extension
FileName, Fileextname = Os.path.splitext (filefullname)
# Save Picture
Simage.save (' {}{}_{}{} '. Format (filedirectory, FileName, ' Nude ' if self.result Else ' Normal ', fileextname))
def del_files (path):
For root, dirs, files in Os.walk (path):
For name in Files:
If Name.endswith (". CR2 "):
Os.remove (Os.path.join (root, name))
Print ("Delete File:" + os.path.join (root, name))
Class Fileeventhandler (FileSystemEventHandler):
def __init__ (self):
Filesystemeventhandler.__init__ (self)
def on_created (self, event):
If Event.is_directory:
Pass
Else
Time.sleep (0.1)
fname = "{0}". Format (Event.src_path)
#fname = "D:/test/new/c.jpg"
If Os.path.isfile (fname):
n = Nude (fname)
N.resize (maxheight=800, maxwidth=600)
N.parse ()
Print (N.result, N.inspect ())
if (N.result):
Print (' true ')
My_file = fname
If Os.path.exists (my_file):
#删除文件
Os.remove (My_file)
Else
Print ("false")
Else
Print (fname, "is not a file")
if __name__ = = "__main__":
Observer = Observer ()
Event_handler = Fileeventhandler ()
Observer.schedule (Event_handler, ' d:\\test ', True)
Observer.start ()
Try
While True:
Time.sleep (1)
Except Keyboardinterrupt:
Observer.stop ()
Observer.join ()
Python automatically recognizes the yellow image