猿问

如何以特定角度旋转图像的坐标(x,y)

为了更好地理解,请在 Jupyternotebook 中重现代码:

我有两个文件:img.jpg 和 img.txt。Img.jpg 是图像,而 img.txt 是面部地标....如果您将它们都绘制出来,它将如下所示:

我将图像旋转了 24.5 度....但是我如何也旋转坐标?

http://img4.mukewang.com/62a847660001879e02470254.jpg

import cv2

img = cv2.imread('img.jpg')

plt.imshow(img)

plt.show()



# In[130]:



landmarks = []

with open('img.txt') as f:

    for line in f:

        landmarks.extend([float(number) for number in line.split()])

landmarks.pop(0) #Remove first line. 

#Store all points inside the variable. 

landmarkPoints = [] #Store the points in this

for j in range(int(len(landmarks))):

    if j%2 == 1:

        continue

    landmarkPoints.append([int(landmarks[j]),int(landmarks[j+1])])



# In[ ]:


def rotate_bound(image, angle):

# grab the dimensions of the image and then determine the

# center

(h, w) = image.shape[:2]

(cX, cY) = (w // 2, h // 2)


# grab the rotation matrix (applying the negative of the

# angle to rotate clockwise), then grab the sine and cosine

# (i.e., the rotation components of the matrix)

M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)

cos = np.abs(M[0, 0])

sin = np.abs(M[0, 1])


# compute the new bounding dimensions of the image

nW = int((h * sin) + (w * cos))

nH = int((h * cos) + (w * sin))


# adjust the rotation matrix to take into account translation

M[0, 2] += (nW / 2) - cX

M[1, 2] += (nH / 2) - cY


# perform the actual rotation and return the image

return cv2.warpAffine(image, M, (nW, nH)) 




# In[131]:



imgcopy = img.copy()

for i in range(len(landmarkPoints)):

    cv2.circle(imgcopy, (landmarkPoints[i][0], landmarkPoints[i][1]), 5, (0, 255, 0), -1)

plt.imshow(imgcopy)

plt.show()

landmarkPoints



# In[146]:



print(img.shape)

print(rotatedImage.shape)



# In[153]:



face_angle = 24.5

rotatedImage = rotate_bound(img, -face_angle)

for i in range(len(landmarkPoints)):

    x,y = (landmarkPoints[i][0], landmarkPoints[i][1])

    cv2.circle(rotatedImage, (int(x),int(y)), 5, (0, 255, 0), -1)

plt.imshow(rotatedImage)

plt.show()

请下载 img.jpg 和 img.txt 以复制此内容:https ://drive.google.com/file/d/1FhQUFvoKi3t7TrIepx2Es0mBGAfT755w/view?usp=sharing



一只萌萌小番薯
浏览 295回答 2
2回答

烙印99

虽然问这个问题已经很久了。但我决定回答它,因为它还没有被接受的答案,即使它是一个被广泛接受的问题。我添加了很多评论以使实现清晰。因此,代码希望是不言自明的。但我也在描述ImageAugmentation's 参数以进一步澄清:这里,original_data_dir是父文件夹的目录,其中所有图像的文件夹都存在(是的,它可以从多个图像文件夹中读取)。这个参数是强制性的。augmentation_data_dir是您要保存输出的文件夹目录。该程序将自动在输出目录中创建所有子文件夹,就像它们出现在输入目录中一样。它是完全可选_augmentation的,它可以通过在输入文件夹名称后面附加字符串来模仿输入目录来生成输出目录。keep_original是另一个可选参数。在许多情况下,您可能希望在输出文件夹中保留原始图像和增强图像。如果需要,请将其设为True(默认)。num_of_augmentations_per_image是要从每个图像生成的增强图像的总数。尽管您只想要旋转,但该程序还旨在进行其他增强,根据需要更改、添加或删除它们。我还添加了一个文档链接,您可以在其中找到可以在此代码中介绍的其他增强功能。默认为3,如果保留原图,3 + 1 = 4输出中会生成图片。discard_overflow_and_underflow用于处理由于空间变换,增强点以及下面的图像可能超出图像分辨率的情况,您可以选择保留它们。但这里默认丢弃。同样,它也会丢弃具有widthorheight值的图像<= 0。默认为True.put_landmarks表示您是否希望在输出中显示地标。制作它True或False根据需要。False默认情况下。希望你喜欢!import loggingimport imgaug as iaimport imgaug.augmenters as iaafrom imgaug.augmentables import Keypointfrom imgaug.augmentables import KeypointsOnImageimport osimport cv2import reSEED = 31 # To reproduce the resultclass ImageAugmentation:&nbsp; &nbsp; def __init__(self, original_data_dir, augmentation_data_dir = None, keep_original = True, num_of_augmentations_per_image = 3, discard_overflow_and_underflow = True, put_landmarks = False):&nbsp; &nbsp; &nbsp; &nbsp; self.original_data_dir = original_data_dir&nbsp; &nbsp; &nbsp; &nbsp; if augmentation_data_dir != None:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.augmentation_data_dir = augmentation_data_dir&nbsp; &nbsp; &nbsp; &nbsp; else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.augmentation_data_dir = self.original_data_dir + '_augmentation'&nbsp; &nbsp; &nbsp; &nbsp; # Most of the time you will want to keep the original images along with the augmented images&nbsp; &nbsp; &nbsp; &nbsp; self.keep_original = keep_original&nbsp; &nbsp; &nbsp; &nbsp; # For example for self.num_of_augmentations_per_image = 3, from 1 image we will get 3 more images, totaling 4 images.&nbsp; &nbsp; &nbsp; &nbsp; self.num_of_augmentations_per_image = num_of_augmentations_per_image&nbsp; &nbsp; &nbsp; &nbsp; # if discard_overflow_and_underflow is True, the program will discard all augmentation where landmark (and image underneath) goes outside of image resolution&nbsp; &nbsp; &nbsp; &nbsp; self.discard_overflow_and_underflow = discard_overflow_and_underflow&nbsp; &nbsp; &nbsp; &nbsp; # Optionally put landmarks on output images&nbsp; &nbsp; &nbsp; &nbsp; self.put_landmarks = put_landmarks&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; def get_base_annotations(self):&nbsp; &nbsp; &nbsp; &nbsp; """This method reads all the annotation files (.txt) and make a list&nbsp; &nbsp; &nbsp; &nbsp; of annotations to be used by other methods.&nbsp; &nbsp; &nbsp; &nbsp; """&nbsp; &nbsp; &nbsp; &nbsp; # base_annotations are the annotations which has come with the original images.&nbsp; &nbsp; &nbsp; &nbsp; base_annotations = []&nbsp; &nbsp; &nbsp; &nbsp; def get_info(content):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; """This utility function reads the content of a single annotation&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; file and returns the count of total number of points and a list of coordinates&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; of the points inside a dictionary.&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; As you have provided in your question, the annotation file looks like the following:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 106&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 282.000000 292.000000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 270.000000 311.000000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 259.000000 330.000000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .....&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .....&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Here, the first line is the number of points.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; The second and the following lines gives their coordinates.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; """&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # As all the lines newline separated, hence splitting them&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # accordingly first&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lines = content.split('\n')&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # The first line is the total count of the point, we can easily get it just by counting the points&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # so we are not taking this information.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # From the second line to the end all lines are basically the coordinate values&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # of each point (in each line). So, going to each of the lines (from the second line)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # and taking the coordinates as tuples.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # We will end up with a list of tuples and which will be inserted to the dict "info"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # under the key "point_coordinates"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; points = []&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for line in lines[1:]:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Now each of the line can be splitted into two numbers representing coordinates&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Keeping inside try block, as some of the lines might be accidentally contain&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # a single number, or it can be the case that there might be some extra newlines&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # where there is no number.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; col, row = line.split(' ')&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; points.append((float(col), float(row)))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; except:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pass&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Returns: List of tuples&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return points&nbsp; &nbsp; &nbsp; &nbsp; for subdir, dirs, files in os.walk(self.original_data_dir):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for file in files:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ext = os.path.splitext(file)[-1].lower()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Looping through image files (instead of annotation files which are in '.txt' format)&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # because image files can have very different extensions and we have to preserve them.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Whereas, all the annotation files are assumed to be in '.txt' format.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Annotation file's (.txt) directory will be generated from here.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ext not in ['.txt']:&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; input_image_file_dir = os.path.join(subdir, file)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # As the image filenames and associated annotation text filenames are the same,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # so getting the common portion of them, it will be used to generate the annotation&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # file's directory.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Also assuming, there are no dots (.) in the input_annotation_file_dir except before the file extension.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; image_annotation_base_dir = self.split_extension(input_image_file_dir)[0]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Generating annotation file's directory&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; input_annotation_file_dir = image_annotation_base_dir + '.txt'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; with open(input_annotation_file_dir, 'r') as f:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; content = f.read()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; image_annotation_base_dir = os.path.splitext(input_annotation_file_dir)[0]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if os.path.isfile(input_image_file_dir):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; image = cv2.imread(input_image_file_dir)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Taking image's shape is basically surving dual purposes.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # First of all, we will need the image's shape for sanity checking after augmentation&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Again, if any of the input image is corrupt this following line will through exception&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # and we will be able to skip that corrput image.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; image_shape = image.shape # height (y), width (x), channels (depth)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Collecting the directories of original annotation files and their contents.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # The same folder structure will be used to save the augmented data.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # As the image filenames and associated annotation text filenames are the same, so&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; base_annotations.append({'image_file_dir': input_image_file_dir,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'annotation_data': get_info(content = content),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'image_resolution': image_shape})&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; except:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; logging.error(f"Unable to read the file: {input_annotation_file_dir}...SKIPPED")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return base_annotations&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; def get_augmentation(self, base_annotation, seed):&nbsp; &nbsp; &nbsp; &nbsp; image_file_dir = base_annotation['image_file_dir']&nbsp; &nbsp; &nbsp; &nbsp; image_resolution = base_annotation['image_resolution']&nbsp; &nbsp; &nbsp; &nbsp; list_of_coordinates = base_annotation['annotation_data']&nbsp; &nbsp; &nbsp; &nbsp; ia.seed(seed)&nbsp; &nbsp; &nbsp; &nbsp; # We have to provide the landmarks in specific format as imgaug requires&nbsp; &nbsp; &nbsp; &nbsp; landmarks = []&nbsp; &nbsp; &nbsp; &nbsp; for coordinate in list_of_coordinates:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # coordinate[0] is along x axis (horizontal axis) and coordinate[1] is along y axis (vertical axis) and (left, top) corner is (0, 0)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; landmarks.append(Keypoint(x = coordinate[0], y = coordinate[1]))&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; landmarks_on_original_img = KeypointsOnImage(landmarks, shape = image_resolution)&nbsp; &nbsp; &nbsp; &nbsp; original_image = cv2.imread(image_file_dir)&nbsp; &nbsp; &nbsp; &nbsp; """&nbsp; &nbsp; &nbsp; &nbsp; Here the magic happens. If you only want rotation then remove other transformations from here.&nbsp; &nbsp; &nbsp; &nbsp; You can even add other various types of augmentation, see documentation here:&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Documentation for image augmentation with keypoints&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; https://imgaug.readthedocs.io/en/latest/source/examples_keypoints.html&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Here you will find other possible transformations&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; https://imgaug.readthedocs.io/en/latest/source/examples_basics.html&nbsp; &nbsp; &nbsp; &nbsp; """&nbsp; &nbsp; &nbsp; &nbsp; seq = iaa.Sequential([&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iaa.Affine(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; scale={"x": (0.8, 1.2), "y": (0.8, 1.2)}, # scale images to 80-120% of their size, individually per axis&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; translate_percent={"x": (-0.2, 0.2), "y": (-0.2, 0.2)}, # translate by -20 to +20 percent (per axis)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rotate=(-90, 90), # rotate by -90 to +90 degrees; for specific angle (say 30 degree) use rotate = (30)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; shear=(-16, 16), # shear by -16 to +16 degrees&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ], random_order=True) # Apply augmentations in random order&nbsp; &nbsp; &nbsp; &nbsp; augmented_image, _landmarks_on_augmented_img = seq(image = original_image, keypoints = landmarks_on_original_img)&nbsp; &nbsp; &nbsp; &nbsp; # Now for maintaining consistency, making the augmented landmarks to maintain same data structure like base_annotation&nbsp; &nbsp; &nbsp; &nbsp; # i.e, making it a list of tuples.&nbsp; &nbsp; &nbsp; &nbsp; landmarks_on_augmented_img = []&nbsp; &nbsp; &nbsp; &nbsp; for index in range(len(landmarks_on_original_img)):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; landmarks_on_augmented_img.append((_landmarks_on_augmented_img[index].x,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;_landmarks_on_augmented_img[index].y))&nbsp; &nbsp; &nbsp; &nbsp; return augmented_image, landmarks_on_augmented_img&nbsp;&nbsp; &nbsp; def split_extension(self, path):&nbsp; &nbsp; &nbsp; &nbsp; # Assuming there is no dots (.) except just before extension&nbsp; &nbsp; &nbsp; &nbsp; # Returns [directory_of_file_without_extension, extension]&nbsp; &nbsp; &nbsp; &nbsp; return os.path.splitext(path)&nbsp;&nbsp; &nbsp; def sanity_check(self, landmarks_aug, image_resolution):&nbsp; &nbsp; &nbsp; &nbsp; # Returns false if the landmark is outside of image resolution.&nbsp; &nbsp; &nbsp; &nbsp; # Or, if the resolution is faulty.&nbsp; &nbsp; &nbsp; &nbsp; for index in range(len(landmarks_aug)):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if landmarks_aug[index][0] < 0 or landmarks_aug[index][1] < 0:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return False&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if landmarks_aug[index][0] >= image_resolution[1] or landmarks_aug[index][1] >= image_resolution[0]:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return False&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if image_resolution[0] <= 0:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return False&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if image_resolution[1] <= 0:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return False&nbsp; &nbsp; &nbsp; &nbsp; return True&nbsp; &nbsp; def serialize(self, serialization_data, image):&nbsp; &nbsp; &nbsp; &nbsp; """This method to write the annotation file and the corresponding image.&nbsp; &nbsp; &nbsp; &nbsp; """&nbsp; &nbsp; &nbsp; &nbsp; # Now it is time to actually writing the image file and the annotation file!&nbsp; &nbsp; &nbsp; &nbsp; # We have to make sure the output folder exists&nbsp; &nbsp; &nbsp; &nbsp; # and "head" is the folder's directory here.&nbsp; &nbsp; &nbsp; &nbsp; image_file_dir = serialization_data['image_file_dir']&nbsp; &nbsp; &nbsp; &nbsp; annotation_file_dir = self.split_extension(image_file_dir)[0] + '.txt'&nbsp; &nbsp; &nbsp; &nbsp; point_coordinates = serialization_data['annotation_data'] # List of tuples&nbsp; &nbsp; &nbsp; &nbsp; total_points = len(point_coordinates)&nbsp; &nbsp; &nbsp; &nbsp; # Getting the corresponding output folder for current image&nbsp; &nbsp; &nbsp; &nbsp; head, tail = os.path.split(image_file_dir)&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; # Creating the folder if it doesn't exist&nbsp; &nbsp; &nbsp; &nbsp; if not os.path.isdir(head):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; os.makedirs(head)&nbsp; &nbsp; &nbsp; &nbsp; # Writing annotation file&nbsp; &nbsp; &nbsp; &nbsp; with open(annotation_file_dir, 'w') as f:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; s = ""&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; s += str(total_points)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; s += '\n'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for point in point_coordinates:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; s += "{:.6f}".format(point[0]) + ' ' + "{:6f}".format(point[1]) + '\n'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f.write(s)&nbsp; &nbsp; &nbsp; &nbsp; if self.put_landmarks:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Optionally put landmarks in the output images.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for index in range(total_points):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cv2.circle(image, (int(point_coordinates[index][0]), int(point_coordinates[index][1])), 2, (255, 255, 0), 2)&nbsp; &nbsp; &nbsp; &nbsp; cv2.imwrite(image_file_dir, image)&nbsp; &nbsp; def augmentat_with_landmarks(self):&nbsp; &nbsp; &nbsp; &nbsp; base_annotations = self.get_base_annotations()&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; for base_annotation in base_annotations:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if self.keep_original == True:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # As we are basically copying the same original data in new directory, changing the original image's directory with the new one with re.sub()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; base_data = {'image_file_dir': re.sub(self.original_data_dir, self.augmentation_data_dir, base_annotation['image_file_dir']),&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'annotation_data': base_annotation['annotation_data']}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.serialize(serialization_data = base_data, image = cv2.imread(base_annotation['image_file_dir']))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for index in range(self.num_of_augmentations_per_image):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Getting a new augmented image in each iteration from the same base image.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Seeding (SEED) for reproducing same result across all execution in the future.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Also seed must be different for each iteration, otherwise same looking augmentation will be generated.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; image_aug, landmarks_aug = self.get_augmentation(base_annotation, seed = SEED + index)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # As for spatial transformations for some images, the landmarks can go outside of the image.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # So, we have to discard those cases (optionally).&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if self.sanity_check(landmarks_aug, base_annotation['image_resolution']) or not self.discard_overflow_and_underflow:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Getting the filename without extension to insert an index number in between to generate a new filename for augmented image&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; filepath_without_ext, ext = self.split_extension(base_annotation['image_file_dir'])&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # As we are writing newly generated images to similar sub folders (just in different base directory)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # that is replacing original_data_dir with augmentation_data_dir.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # So, to do this we are using, re.sub(what_to_replace, with_which_to_replace, from_where_to_replace)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; filepath_for_aug_img_without_ext = re.sub(self.original_data_dir, self.augmentation_data_dir, filepath_without_ext)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new_filepath_wo_ext = filepath_for_aug_img_without_ext + '_' + str(index)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; augmentation_data = {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'image_file_dir': new_filepath_wo_ext + ext,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'annotation_data': landmarks_aug&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.serialize(serialization_data = augmentation_data, image = image_aug)# Make put_landmarks = False if you do not want landmarks to be shown in output# original_data_dir is the single parent folder directory inside of which all image folder(s) exist.img_aug = ImageAugmentation(original_data_dir = 'parent/folder/directory/of/img/folder', put_landmarks = True)&nbsp;img_aug.augmentat_with_landmarks()以下是代码示例输出的快照:请注意,我使用了一个 package&nbsp;imgaug。我会建议您安装该0.4.0版本,因为我发现它可以正常工作。请参阅此处的原因,这是公认的答案。

阿波罗的战车

当你尝试这样的事情时,选择合适的坐标系非常重要。在您的情况下,您必须将原点放在(0,0)图像的中心。将旋转应用于以原点为中心的坐标后,面部点将在新图像上正确对齐。
随时随地看视频慕课网APP

相关分类

Python
我要回答