日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python dlib学习(十):换脸

發布時間:2025/3/21 python 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python dlib学习(十):换脸 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

這次再用dlib來做一個很酷的應用:換臉。在百度可以搜出一大堆轉載的,里面雖然講的不是很詳細(數學部分),個人感覺大多數人對于奇異值分解仿射變換矩陣 怎么實現根本不敢興趣,只想上代碼實現功能,所以后面就省去了數學的那部分。
一篇文章的鏈接:教你用200行Python代碼“換臉”
代碼的github鏈接:https://github.com/matthewearl/faceswap/blob/master/faceswap.py
我很大程度上都是參考其中的代碼,但是有些部分會不太一樣。完整工程下載鏈接在文章的最后。

功能實現

第一步,我們要把零散的功能全部實現,后面驗證都正確后,再把這些“零件”拼在一起。

導入包和定義路徑

#coding=utf-8 import cv2 import dlib import os import numpy as np import globcurrent_path = os.getcwd() # 獲取當前路徑 predictor_68_points_path = current_path + '/model/shape_predictor_68_face_landmarks.dat' predictor_5_points_path = current_path + '/model/shape_predictor_5_face_landmarks.dat' predictor_path = predictor_68_points_path# 選取人臉68個特征點檢測器 face_path = current_path + '/faces/'

導入包,然后獲取當前路徑。指定要用到的模型文件和測試圖片的路徑。這里的模型文件有兩個,一個是人臉的68個特征點的檢測器(shape_predictor_68_face_landmarks.dat),一個是5個特征點的檢測器(shape_predictor_5_face_landmarks.dat)。自行選擇即可。

獲取特征點

程序實現

detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor(predictor_path)class TooManyFaces(Exception):passclass NoFace(Exception):passdef get_landmark(image):face_rect = detector(image, 1)if len(face_rect) > 1:print('Too many faces.We only need no more than one face.')raise TooManyFaceselif len(face_rect) == 0:print('No face.We need at least one face.')raise NoFaceelse:print('left {0}; top {1}; right {2}; bottom {3}'.format(face_rect[0].left(), face_rect[0].top(), face_rect[0].right(), face_rect[0].bottom()))# box = face_rect[0]# shape = predictor(image, box)# return np.matrix([[p.x, p.y] for p in shape.parts()])return np.matrix([[p.x, p.y] for p in predictor(image, face_rect[0]).parts()])
  • 獲取人臉的特征點的這些套路不愿意再重復介紹了,前面的博客都有講。偷個懶,直接上鏈接:python dlib學習(二):人臉特征點標定。
  • 有兩個異常類:NoFace和TooManyFaces,分別對應沒有檢測到人臉和檢測到超過一個人的臉。我們只是實現簡單的換臉,只需要圖片中有一張臉就足夠了,如果不符合情況就拋出異常。
  • 還有一點,由于后面涉及矩陣計算,為了加快計算,把得到的這些特征點轉換成numpy矩陣。

編寫測試函數

接下來測試一下這段程序,編寫一段測試程序。比較簡單,直接貼代碼了:

def test_get_landmark():for img_path in glob.glob(os.path.join(face_path, "*.jpg")):print("Processing file: {}".format(img_path))img = cv2.imread(img_path, cv2.IMREAD_COLOR)img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)landmarks = get_landmark(img)print landmarks

運行程序,然后會讀取前面指定的faces文件夾中的所有圖片:
如果圖片只有一張圖片,那么會正常打印出如下信息(人臉位置、所有檢測到的特征點等等):

我故意放了一張有多個人臉的圖片,那么程序會拋出異常直接終止:

測試通過后,再往下寫程序。

使用普氏分析(Procrustes analysis)調整臉部

程序實現

因為圖片中的人臉可能會有一定的傾斜,而且不同圖片中人臉的位置也不一樣。所以,我們需要把人臉的位置進行調整。

def transformation_from_points(points1, points2):points1 = points1.astype(np.float64)points2 = points2.astype(np.float64)c1 = np.mean(points1, axis=0)c2 = np.mean(points2, axis=0)points1 -= c1points2 -= c2s1 = np.std(points1)s2 = np.std(points2)points1 /= s1points2 /= s2U, S, Vt = np.linalg.svd(points1.T * points2)R = (U * Vt).Treturn np.vstack([np.hstack(((s2 / s1) * R, c2.T - (s2 / s1) * R * c1.T)), np.matrix([0., 0., 1.])])

這部分會比較難懂,下面是我直接從前面那篇文章摘出的相關描述。
代碼分別實現了下面幾步:

  • 將輸入矩陣轉換為浮點數。這是之后步驟的必要條件。
  • 每一個點集減去它的矩心。一旦為這兩個新的點集找到了一個最佳的縮放和旋轉方法,這兩個矩心c1和c2就可以用來找到完整的解決方案。
  • 同樣,每一個點集除以它的標準偏差。這消除了問題的組件縮放偏差。
  • 使用Singular Value Decomposition計算旋轉部分。可以在維基百科上看到關于解決正交普氏問題的細節(https://en.wikipedia.org/wiki/Orthogonal_Procrustes_problem)。
  • 利用仿射變換矩陣(https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations)返回完整的轉化。

實質上最后就是得到了一個轉換矩陣,第一幅圖片中的人臉可以通過這個轉換矩陣映射到第二幅圖片中,與第二幅圖片中的人臉對應。(吐槽:奇異值分解直接上numpy解出來,真是省了不少事啊)
得到了轉換矩陣后,就可以使用它進行映射了吧:

def wrap_image(image, M, dshape):output_image = np.zeros(dshape, dtype=image.dtype)cv2.warpAffine(image, M[:2], (dshape[1], dshape[0]), dst=output_image, flags=cv2.WARP_INVERSE_MAP, borderMode=cv2.BORDER_TRANSPARENT)return output_image

這里也是使用了opencv的warpAffine函數,自己從底層實現會比較復雜。python代碼也得以精簡。

測試函數

def test_warp_image():jobs_image_path = os.path.join(face_path, "jobs2.jpg")obama_image_path = os.path.join(face_path, "obama.jpg")jobs_img = cv2.imread(jobs_image_path, cv2.IMREAD_COLOR)cv2.imshow("jobs_img", jobs_img)obama_img = cv2.imread(obama_image_path, cv2.IMREAD_COLOR)cv2.imshow("obama_img", obama_img)jobs_landmark = get_landmark(jobs_img)obama_landmark = get_landmark(obama_img)transformation_matrix = transformation_from_points(jobs_landmark, obama_landmark)print('warpping images...')output_image = warp_image(obama_img, transformation_matrix, dshape=jobs_img.shape)print('showing the results!') print('Please press any button to continue.')cv2.namedWindow("output_image", cv2.WINDOW_AUTOSIZE)cv2.imshow("output_image", output_image)cv2.waitKey(0)cv2.destroyAllWindows()

直接看結果,很直觀:

獲取人臉掩模

我們已經得到了對齊后的人臉圖片,那么接下來的目標就是得到人臉的位置。我們可以使用一個掩模(mask)來表示,屬于人臉的區域像素值為1,不屬于人臉的區域像素值為0。在提取時我們直接將原圖片乘以掩模,就可以得到人臉,而其余區域像素值為0;如果將原圖片乘以1?mask,即人臉區域會是0,其余區域會保留下來。上面這兩個結果相加,既可以實現初步的換臉。

程序實現

# 人臉特征點對應的器官 LEFT_EYE_POINTS = list(range(42, 48)) RIGHT_EYE_POINTS = list(range(36, 42)) LEFT_BROW_POINTS = list(range(22, 27)) RIGHT_BROW_POINTS = list(range(17, 22)) NOSE_POINTS = list(range(27, 35)) MOUTH_POINTS = list(range(48, 61)) OVERLAY_POINTS = [LEFT_EYE_POINTS + RIGHT_EYE_POINTS + LEFT_BROW_POINTS + RIGHT_BROW_POINTS,NOSE_POINTS + MOUTH_POINTS, ] FEATHER_AMOUNT = 11# 繪制凸包 def draw_convex_hull(img, points, color):points = cv2.convexHull(points)cv2.fillConvexPoly(img, points, color)# 獲取人臉掩模 def get_face_mask(img, landmarks):img = np.zeros(img.shape[:2], dtype=np.float64)for group in OVERLAY_POINTS:draw_convex_hull(img, landmarks[group], color=1)img = np.array([img, img, img]).transpose((1, 2, 0)) img = (cv2.GaussianBlur(img, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) > 0) * 1.0img = cv2.GaussianBlur(img, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0)return img

測試函數

def test_face_mask():jobs_image_path = os.path.join(face_path, "jobs.jpg")obama_image_path = os.path.join(face_path, "obama.jpg")jobs_img = cv2.imread(jobs_image_path, cv2.IMREAD_COLOR)obama_img = cv2.imread(obama_image_path, cv2.IMREAD_COLOR)jobs_landmark = get_landmark(jobs_img)obama_landmark = get_landmark(obama_img)jobs_mask = get_face_mask(jobs_img, jobs_landmark)obama_mask = get_face_mask(obama_img, obama_landmark)cv2.imshow("jobs_img", jobs_img)cv2.imshow("obama_img", obama_img)cv2.imshow("jobs_mask", jobs_mask)cv2.imshow("obama_mask", obama_mask)cv2.waitKey(0)cv2.destroyAllWindows()

運行結果,很直觀:

顏色校正

有了前面的函數,其實我們已經實現了換臉的大部分功能了。人臉已經對齊了,我們有人臉的特征點,可以進行凸包檢測來得到人臉的區域,然后把第二幅圖凸包中的位置摳出來放到第一幅圖片中,就可以了。但是,這樣得到的結果是十分難看的,因為背景光照或者膚色等等因素的影響,看起來會十分不自然。

程序實現

COLOUR_CORRECT_BLUR_FRAC = 0.6 LEFT_EYE_POINTS = list(range(42, 48)) RIGHT_EYE_POINTS = list(range(36, 42))def correct_colours(im1, im2, landmarks1):blur_amount = COLOUR_CORRECT_BLUR_FRAC * np.linalg.norm(np.mean(landmarks1[LEFT_EYE_POINTS], axis=0) -np.mean(landmarks1[RIGHT_EYE_POINTS], axis=0))blur_amount = int(blur_amount)if blur_amount % 2 == 0:blur_amount += 1im1_blur = cv2.GaussianBlur(im1, (blur_amount, blur_amount), 0)im2_blur = cv2.GaussianBlur(im2, (blur_amount, blur_amount), 0)# Avoid divide-by-zero errors.im2_blur += (128 * (im2_blur <= 1.0)).astype(im2_blur.dtype)return (im2.astype(np.float64) * im1_blur.astype(np.float64) /im2_blur.astype(np.float64))

實現思路就是利用高斯模糊來幫助我們校正顏色,使用im2除以im2的高斯模糊,乘以im1來校正顏色。總體來說,這個方法比較粗糙和暴力,很多因素都忽略了。結果也只能從一定程度上獲得提高,有時反而會被修正的更“差”。因為有很多的影響因素,選取到一個合適的高斯核的大小,才可能取得比較理想的結果。如果太小,第一個圖像的面部特征將顯示在第二個圖像中。過大,內核之外區域像素被覆蓋,并發生變色。這里的內核用了一個0.6 *的瞳孔距離。

測試函數

這里的測試函數會用到前面所有的程序,并把過程中生成的圖片全部打印出來。
注:在進行一系列的操作后,我們程序中的圖像的灰度不是初始的0-255或是0-1了,我們要顯示這些圖片必須要先進行歸一化。我在程序中調用了opencv中的cv2.normalize()函數實現。

def test_all():jobs_image_path = os.path.join(face_path, "jobs.jpg")obama_image_path = os.path.join(face_path, "obama.jpg")jobs_img = cv2.imread(jobs_image_path, cv2.IMREAD_COLOR)jobs_img = cv2.resize(jobs_img, (jobs_img.shape[1] * SCALE_FACTOR,jobs_img.shape[0] * SCALE_FACTOR))cv2.imshow("1", jobs_img)cv2.waitKey(0)# img1 = cv2.cvtColor(jobs_img, cv2.COLOR_BGR2RGB)img1 = jobs_imgobama_img = cv2.imread(obama_image_path, cv2.IMREAD_COLOR)obama_img = cv2.resize(obama_img, (obama_img.shape[1] * SCALE_FACTOR,obama_img.shape[0] * SCALE_FACTOR))cv2.imshow("2", obama_img)cv2.waitKey(0)# img2 = cv2.cvtColor(obama_img, cv2.COLOR_BGR2RGB)img2 = obama_imglandmark1 = get_landmark(img1)landmark2 = get_landmark(img2)transformation_matrix = transformation_from_points(landmark1[ALIGN_POINTS], landmark2[ALIGN_POINTS])mask = get_face_mask(img2, landmark2)cv2.imshow("3", mask)cv2.waitKey(0)warped_mask = warp_image(mask, transformation_matrix, img1.shape)cv2.imshow("4", warped_mask)cv2.waitKey(0)combined_mask = np.max([get_face_mask(img1, landmark1), warped_mask], axis=0)cv2.imshow("5", combined_mask)cv2.waitKey(0)warped_img2 = warp_image(img2, transformation_matrix, img1.shape)cv2.imshow("6", warped_img2)cv2.waitKey(0)# warped_corrected_img2 = correct_colours(img1, warped_img2, landmark1)warped_corrected_img2 = correct_colours(img1, warped_img2, landmark1)warped_corrected_img2_temp = np.zeros(warped_corrected_img2.shape, dtype=warped_corrected_img2.dtype)cv2.normalize(warped_corrected_img2, warped_corrected_img2_temp, 0, 1, cv2.NORM_MINMAX)cv2.imshow("7", warped_corrected_img2_temp)cv2.waitKey(0)output = img1 * (1.0 - combined_mask) + warped_corrected_img2 * combined_maskcv2.normalize(output, output, 0, 1, cv2.NORM_MINMAX)cv2.imshow("8", output.astype(output.dtype))cv2.waitKey(0)cv2.destroyAllWindows()

運行程序后,每次按任意鍵就會顯示下一步的結果,下面是截圖。

原圖片1:

原圖片2:

提取圖片2掩模:

將圖片2掩模映射到圖片1:

將映射后的掩模與圖片1的原始掩模融合:

將圖片2映射到圖片1:

顏色校正:

使用前面的掩模合成圖片:

封裝

功能實現了,但是都是函數形式,也不利于調用,可擴展性也比較差。我把前面的程序整合了一下封裝成了類,可以更容易地使用。
文件名:FaceChanger.py

#coding=utf-8 import cv2 import dlib import os import numpy as np import globclass TooManyFaces(Exception):passclass NoFace(Exception):passclass FaceChanger(object):def __init__(self, which_predictor='68'):print('Starting your FaceChanger...')self.current_path = os.getcwd()print('Current path:{0}'.format(self.current_path))predictor_68_points_path = self.current_path + '/model/shape_predictor_68_face_landmarks.dat'predictor_5_points_path = self.current_path + '/model/shape_predictor_5_face_landmarks.dat'if which_predictor == '68':predictor_name = 'shape_predictor_68_face_landmarks.dat'self.predictor_path = predictor_68_points_pathelif which_predictor == '5':predictor_name = 'shape_predictor_5_face_landmarks.dat'self.predictor_path = predictor_5_points_pathelse:predictor_name = 'shape_predictor_68_face_landmarks.dat'self.predictor_path = predictor_68_points_pathprint('Your predictor is:{0}'.format(predictor_name))print('Searching for faces...')self.face_path = self.current_path + '/faces/'self.face_list = glob.glob(os.path.join(self.face_path, '*.jpg'))print('{0} faces have been found.'.format(len(self.face_list)))print('Here are the names of those pictures:')name = self.face_list[0].strip().split('/')[-1]for face_file in self.face_list[1:]:name += ' ' + face_file.strip().split('/')[-1]print('%s'%(name))print('You can choose two of theses pictures, and change the face between them.')# some parametersself.SCALE_FACTOR = 1 self.FEATHER_AMOUNT = 11self.FACE_POINTS = list(range(17, 68))self.MOUTH_POINTS = list(range(48, 61))self.RIGHT_BROW_POINTS = list(range(17, 22))self.LEFT_BROW_POINTS = list(range(22, 27))self.RIGHT_EYE_POINTS = list(range(36, 42))self.LEFT_EYE_POINTS = list(range(42, 48))self.NOSE_POINTS = list(range(27, 35))self.JAW_POINTS = list(range(0, 17))# Points used to line up the images.self.ALIGN_POINTS = (self.LEFT_BROW_POINTS + self.RIGHT_EYE_POINTS + self.LEFT_EYE_POINTS +self.RIGHT_BROW_POINTS + self.NOSE_POINTS + self.MOUTH_POINTS)# Points from the second image to overlay on the first. The convex hull of each# element will be overlaid.self.OVERLAY_POINTS = [self.LEFT_EYE_POINTS + self.RIGHT_EYE_POINTS + self.LEFT_BROW_POINTS + self.RIGHT_BROW_POINTS,self.NOSE_POINTS + self.MOUTH_POINTS,]self.COLOUR_CORRECT_BLUR_FRAC = 0.6# load in modelsself.detector = dlib.get_frontal_face_detector()self.predictor = dlib.shape_predictor(self.predictor_path)self.image1 = Noneself.image2 = Noneself.landmarks1 = Noneself.landmarks2 = Nonedef load_images(self, image1_name, image2_name):assert image1_name.strip().split('.')[-1] == 'jpg'assert image2_name.strip().split('.')[-1] == 'jpg'image1_path = os.path.join(self.face_path, image1_name)image2_path = os.path.join(self.face_path, image2_name)self.image1 = cv2.imread(image1_path, cv2.IMREAD_COLOR)self.image2 = cv2.imread(image2_path, cv2.IMREAD_COLOR)self.landmarks1 = self.get_landmark(self.image1)self.landmarks2 = self.get_landmark(self.image2)def run(self, showProcedure=False, saveResult=True):if self.image1 is None or self.image2 is None:print('You need to load two images first.')returnif showProcedure == True:print('Showing the procedure.Press any key to continue your process.')cv2.imshow("1", self.image1)cv2.waitKey(0)cv2.imshow("2", self.image2)cv2.waitKey(0)M = self.transformation_from_points(\self.landmarks1[self.ALIGN_POINTS], self.landmarks2[self.ALIGN_POINTS])mask = self.get_face_mask(self.image2, self.landmarks2)if showProcedure == True:cv2.imshow("3", mask)cv2.waitKey(0)warped_mask = self.warp_image(mask, M, self.image1.shape)if showProcedure == True:cv2.imshow("4", warped_mask)cv2.waitKey(0)combined_mask = np.max([self.get_face_mask(self.image1, self.landmarks1), \warped_mask], axis=0)if showProcedure == True:cv2.imshow("5", combined_mask)cv2.waitKey(0)warped_img2 = self.warp_image(self.image2, M, self.image1.shape)if showProcedure == True:cv2.imshow("6", warped_img2)cv2.waitKey(0)warped_corrected_img2 = self.correct_colours(self.image1, warped_img2, self.landmarks1)warped_corrected_img2_temp = np.zeros(warped_corrected_img2.shape, dtype=warped_corrected_img2.dtype)cv2.normalize(warped_corrected_img2, warped_corrected_img2_temp, 0, 1, cv2.NORM_MINMAX)if showProcedure == True:cv2.imshow("7", warped_corrected_img2_temp)cv2.waitKey(0)output = self.image1 * (1.0 - combined_mask) + warped_corrected_img2 * combined_maskoutput_show = np.zeros(output.shape, dtype=output.dtype)cv2.normalize(output, output_show, 0, 1, cv2.NORM_MINMAX)cv2.normalize(output, output, 0, 255, cv2.NORM_MINMAX)if showProcedure == True:cv2.imshow("8", output_show.astype(output_show.dtype))cv2.waitKey(0)cv2.destroyAllWindows()if saveResult is True:cv2.imwrite("output.jpg", output)def get_landmark(self, image):face_rect = self.detector(image, 1)if len(face_rect) > 1:print('Too many faces.We only need no more than one face.')raise TooManyFaceselif len(face_rect) == 0:print('No face.We need at least one face.')raise NoFaceelse:print('left {0}; top {1}; right {2}; bottom {3}'.format(face_rect[0].left(), face_rect[0].top(), face_rect[0].right(), face_rect[0].bottom()))# box = face_rect[0]# shape = predictor(image, box)# return np.matrix([[p.x, p.y] for p in shape.parts()])return np.matrix([[p.x, p.y] for p in self.predictor(image, face_rect[0]).parts()])def transformation_from_points(self, points1, points2):points1 = points1.astype(np.float64)points2 = points2.astype(np.float64)c1 = np.mean(points1, axis=0)c2 = np.mean(points2, axis=0)points1 -= c1points2 -= c2s1 = np.std(points1)s2 = np.std(points2)points1 /= s1points2 /= s2U, S, Vt = np.linalg.svd(points1.T * points2)R = (U * Vt).Treturn np.vstack([np.hstack(((s2 / s1) * R, c2.T - (s2 / s1) * R * c1.T)), np.matrix([0., 0., 1.])])def warp_image(self, image, M, dshape):output_image = np.zeros(dshape, dtype=image.dtype)cv2.warpAffine(image, M[:2], (dshape[1], dshape[0]), dst=output_image, flags=cv2.WARP_INVERSE_MAP, borderMode=cv2.BORDER_TRANSPARENT)return output_imagedef correct_colours(self, im1, im2, landmarks1):blur_amount = self.COLOUR_CORRECT_BLUR_FRAC * np.linalg.norm(np.mean(landmarks1[self.LEFT_EYE_POINTS], axis=0) -np.mean(landmarks1[self.RIGHT_EYE_POINTS], axis=0))blur_amount = int(blur_amount)if blur_amount % 2 == 0:blur_amount += 1im1_blur = cv2.GaussianBlur(im1, (blur_amount, blur_amount), 0)im2_blur = cv2.GaussianBlur(im2, (blur_amount, blur_amount), 0)# Avoid divide-by-zero errors.im2_blur += (128 * (im2_blur <= 1.0)).astype(im2_blur.dtype)return (im2.astype(np.float64) * im1_blur.astype(np.float64) /im2_blur.astype(np.float64))def draw_convex_hull(self, img, points, color):points = cv2.convexHull(points)cv2.fillConvexPoly(img, points, color)def get_face_mask(self, img, landmarks):img = np.zeros(img.shape[:2], dtype=np.float64)for group in self.OVERLAY_POINTS:self.draw_convex_hull(img, landmarks[group], color=1)img = np.array([img, img, img]).transpose((1, 2, 0)) img = (cv2.GaussianBlur(img, (self.FEATHER_AMOUNT, self.FEATHER_AMOUNT), 0) > 0) * 1.0img = cv2.GaussianBlur(img, (self.FEATHER_AMOUNT, self.FEATHER_AMOUNT), 0)return img

這段代碼就可以直接使用了。
調用方法示例如下:

from FaceChanger import * fc = FaceChanger() fc.load_images('ibrahimovic.jpg', 'pique.jpg') fc.run(showProcedure=True)
  • 創建類的實例;
  • 導入兩張圖片;
  • 運行即可(可以選擇是否顯示過程中的圖片);
  • 如果選擇顯示圖片,會顯示如下結果。圖片8是合成的圖片。最后還會在當前文件夾保存生成的圖片。

    完整工程下載鏈接:http://download.csdn.net/download/hongbin_xu/10170440。

    總結

    以上是生活随笔為你收集整理的python dlib学习(十):换脸的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 国产乱国产乱300精品 | 丰满大爆乳波霸奶 | 日韩专区在线观看 | 国产欧美日韩激情 | 91丨九色丨蝌蚪丨丝袜 | 麻豆乱淫一区二区三区 | 国产精品27p | 四虎精品在永久在线观看 | 六月婷婷网| 成人黄色免费观看 | 尤物视频在线 | 国内精品嫩模av私拍在线观看 | 亚洲经典自拍 | 久久久高清免费视频 | 在线干 | 精品久久人妻av中文字幕 | 秋霞欧美一区二区三区视频免费 | 亚洲精品福利 | 日韩九九九 | 性xxxxx大片免费视频 | 国产人妖一区二区 | 游戏涩涩免费网站 | 欧美粗又大 | 538国产精品一区二区免费视频 | 国产伦精品一区二区三区免.费 | 免费观看视频一区 | 欧美黑人孕妇孕交 | 黄色免费在线观看视频 | 欧美性猛交xxx乱久交 | 女人黄色片| 亚洲成人tv| aaa特级毛片 | 校园春色 亚洲色图 | 黄色片久久久 | 女性向av免费网站 | 国产a网站 | 日日噜 | 青青艹在线视频 | av天堂一区二区 | 国产精品第十页 | 欧美做爰爽爽爽爽爽爽 | 秋霞av一区二区三区 | 欧美日韩国语 | 萌白酱喷水视频 | 午夜精品福利一区二区蜜股av | 亚洲av永久纯肉无码精品动漫 | 国产在线视频自拍 | 在线观看国产精品入口男同 | 91久色蝌蚪 | 动漫美女舌吻 | 成人福利社 | 蜜桃精品久久久久久久免费影院 | 理论片国产| 色图网址| 欧美日韩国产高清 | 香蕉久久a毛片 | 都市激情自拍偷拍 | 午夜影院视频 | 日本午夜激情 | 看av免费毛片手机播放 | 国产欧美综合在线 | 午夜性片 | 最色网站 | 久久精品毛片 | 国产偷国产偷av亚洲清高 | av首页在线观看 | 国产亚洲片 | 手机在线免费av | 毛片视| 农村脱精光一级 | 麻豆 美女 丝袜 人妻 中文 | 97在线精品视频 | av电影在线观看不卡 | 天海翼一二三区 | 成人做受视频试看60秒 | 亚洲av无码国产精品久久久久 | 九色porn蝌蚪 | 天天干天天色天天射 | 欧美激情亚洲激情 | 青青91| av导航网址 | 手机亚洲第一页 | 网站在线观看你懂的 | v天堂在线观看 | 欧美男女性生活视频 | av网站在线免费 | 国产一级做a爰片久久毛片男 | 黑料视频在线观看 | 国产婷婷色一区二区三区在线 | 爽爽爽av| 懂色aⅴ一区二区三区免费 国产精品99在线观看 | 2025国产精品视频 | 超碰在线91 | 欧美黄色片网站 | 国产精品福利网站 | 国产精品久久久久久久久久久久久 | 羞羞答答av | 91抖音在线观看 | 欧美三级在线看 |