"Selbstporträt (Körper)" ist eine Gewohnheit vieler Auszubildender (Menschen, die Muskeltraining lieben). Es ist eine glückselige Zeit, nach dem Training ein Foto des gepumpten Körpers zu machen und später darauf zurückzublicken. Wenn Sie das aufgenommene Bild in einer Animation wie im Zeitraffer anzeigen, können Sie außerdem feststellen, dass das Muskelwachstum besser ausgewählt werden kann! Dieser Artikel verwendet tiefes Lernen, um den Zeitraffer des Körpers dramatisch zu verbessern.
Änderungen im Gremium von Dezember 2017 bis März 2020
Ich habe aus den aufgenommenen Bildern einen Zeitraffer erstellt. Ich war jedoch besorgt über die Lücke zwischen den Bildern und habe sie manuell korrigiert, um einen reibungslosen Zeitraffer zu erzielen. Um die Probleme der manuellen Arbeit zu vermeiden, wurde die Korrektur automatisch mithilfe von Deep Learning durchgeführt.
Lassen Sie uns vorerst einen Zeitraffer erstellen, der das Bild so wechselt, wie es kontinuierlich ist.
Code zur Erstellung von Zeitraffern (Teil)
#Sie können Videos mit opencv machen,
#So erstellen Sie eine mp4-Datei, die in der Google Colab-Umgebung auf Zwietracht abgespielt werden kann
#Ich habe es genossen, Skvideo zu benutzen.
import skvideo.io
def create_video(imgs, out_video_path, size_wh):
video = []
vid_out = skvideo.io.FFmpegWriter(out_video_path,
inputdict={
"-r": "10"
},
outputdict={
"-r": "10"
})
for img in imgs:
img = cv2.resize(img, size_wh)
vid_out.writeFrame(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
vid_out.close()
imgs = load_images("images_dir")
create_video(imgs, "video.mp4", (w,h))
Das Ergebnis ist wie folgt.
Ich mache mir Sorgen um die Lücke und kann mich nicht auf mein Kind (Körper) konzentrieren.
Ich möchte diese Lücke irgendwie leicht schließen. Wenn ich irgendwo auf meinem Körper einen Referenzpunkt setze und ihn repariere, habe ich in etwa 0,1 Sekunden die Lösung von "Brustwarze" und "Nabel" gefunden. Hier erfahren Sie, wie Sie Ihre Brustwarzen und Ihren Nabel reparieren.
Erstellen Sie zunächst ein Werkzeug, das der Brustwarze und dem Nabel UV-Koordinaten gibt. Es mag möglich sein, es mithilfe von cvat usw. zu realisieren, aber als ich die Zeit für die Beherrschung und die Zeit für die Erstellung meines eigenen Werkzeugs schätzte, kam ich zu dem Schluss, dass es schneller ist, es selbst zu erstellen, also habe ich es erstellt.
Die Spezifikation des Werkzeugs ist, dass, wenn Sie einen Ordner angeben, die Bilder fortlaufend angezeigt werden. Klicken Sie also für jedes Bild auf die drei Punkte des Nippels und des Nabels, und die angeklickten Koordinaten werden in die CSV-Datei ausgegeben. Werden. Ich habe tkinter für die GUI verwendet (die Quelle ist abgekürzt).
Die Position der Brustwarze und des Nabels wird durch affine Umwandlung gemäß dem ersten Bild festgelegt.
Korrigierter Zeitraffer-Erstellungscode (Teil)
def p3affine_img(img, src_p, dst_p):
h, w, ch = img.shape
pts1 = np.float32([src_p[0],src_p[1],src_p[2]])
pts2 = np.float32([dst_p[0],dst_p[1],dst_p[2]])
M = cv2.getAffineTransform(pts1,pts2)
dst = cv2.warpAffine(img,M,(h, w))
return dst
df = read_annotationd() #Kürzung
imgs = []
src_p = None
for index, row in df.iterrows():
img = cv2.imread(row.file)
dst_p = [ [row.p1x, row.p1y], #Linke Brustwarze
[row.p2x, row.p2y], #Rechter Nippel
[row.p3x, row.p3y]] #Nabel
if src_p is None:
src_p = dst_p
else:
img = p3affine_img(img, dst_p, src_p)
imgs.append(img)
write_video(imgs) #Kürzung
Die Ergebnisse sind wie folgt.
Ich konnte den erwarteten Zeitraffer machen, Glückwunsch. ** Nicht! ** ** **
Die Anzahl der Blätter, für die diesmal Koordinaten angegeben werden, beträgt 120 (Zeitraum vom 9. September 2019 bis März 2020). Ich habe jedoch immer noch 281 Bilder, die ich seit Dezember 2017 aufgenommen habe und denen keine Koordinaten zugewiesen wurden. Darüber hinaus müssen wir in den nächsten Jahrzehnten Muskeltraining durchführen, dh über Jahrzehnte hinweg weiterhin Koordinaten angeben. Selbst wenn man sich nur vorstellt, wird Cortisol ausgeschieden und fällt in den Stoffwechsel. Ich dachte darüber nach, Zucker zu ergänzen, um dies zu lösen.
Das stimmt ~~ Lass uns ins Fitnessstudio gehen ~~ Deep Learning.
Erstellen Sie ein Modell, das die Positionen der "Brustwarze" und des "Nabels" schätzt. Wenn dies erreicht ist, müssen Sie lediglich die Affin-Konvertierung wie zuvor anwenden. Wir betrachten die Erkennung von Brustwarzen und Nabel als Segmentierungsaufgabe. Die Erkennung von Schlüsselpunkten wie die Haltungsschätzung scheint besser zu sein, aber ich persönlich habe mehr Erfahrung mit Segmentierungsaufgaben, deshalb habe ich mich dafür entschieden.
Der Datensatz ist wie folgt. Da 2019/9 bis 2020/3 bereits Koordinaten zugewiesen wurden, werden diese für Trainingsbilder und Verifizierungsbilder verwendet, um automatisch Koordinaten für den verbleibenden Zeitraum zu erhalten.
Es ist denkbar, durch 4 Klassifikationen von "rechter Nippel", "linker Nippel", "Nabel" und "Hintergrund" zu lösen, aber dieses Mal haben wir beschlossen, 2 Klassifikationen von "rechter Nippel, linker Nippel, Nabel" und "Hintergrund" zu verwenden. Ich dachte, es wäre einfach, sie auf Regelbasis zu klassifizieren, solange ich drei Punkte erkennen könnte. Lassen Sie uns nun ein Maskenbild erstellen. Machen Sie die Koordinatenpunkte basierend auf den zuvor erstellten Koordinatendaten etwas größer und füllen Sie sie mit 1. Ansonsten ist es der Hintergrund, setzen Sie ihn also auf 0.
for index, row in df.iterrows():
file = row.file
mask = np.zeros((img_h, img_w), dtype=np.uint8)
mask = cv2.circle(mask,(row.p1x, row.p1y,), 15, (1), -1)
mask = cv2.circle(mask,(row.p2x, row.p2y,), 15, (1), -1)
mask = cv2.circle(mask,(row.p3x, row.p3y,), 15, (1), -1)
save_img(mask, row.file) #Kürzung
Visuell (1 ist weiß, 0 ist schwarz) sind die Daten wie folgt.
Bilden Sie diese Paare mit dem physischen Bild.
Zum Lernen habe ich DeepLab v3 (Torch Vision) verwendet. Die 120 Bilder wurden zur Schulung und Überprüfung im Verhältnis 8: 2 aufgeteilt. Obwohl die Anzahl der Blätter recht gering ist, haben wir die Daten aus folgenden Gründen nicht erweitert.
Ich denke jedoch, dass es besser ist, die Daten zu erweitern (es ist einfach nicht ärgerlich).
Datensatzklasse / lernbezogene Funktionen
class MaskDataset(Dataset):
def __init__(self, imgs_dir, masks_dir, scale=1, transforms=None):
self.imgs_dir = imgs_dir
self.masks_dir = masks_dir
self.imgs = list(sorted(glob.glob(os.path.join(imgs_dir, "*.jpg "))))
self.msks = list(sorted(glob.glob(os.path.join(masks_dir, "*.png "))))
self.transforms = transforms
self.scale = scale
def __len__(self):
return len(self.imgs_dir)
@classmethod
def preprocess(cls, pil_img, scale):
#Es sieht gut aus für Graustufen, aber es ist mühsam, also werde ich nicht
# pil_img = pil_img.convert("L")
w, h = pil_img.size
newW, newH = int(scale * w), int(scale * h)
pil_img = pil_img.resize((newW, newH))
img_nd = np.array(pil_img)
if len(img_nd.shape) == 2:
img_nd = np.expand_dims(img_nd, axis=2)
# HWC to CHW
img_trans = img_nd.transpose((2, 0, 1))
if img_trans.max() > 1:
img_trans = img_trans / 255
return img_trans
def __getitem__(self, i):
mask_file = self.msks[i]
img_file = self.imgs[i]
mask = Image.open(mask_file)
img = Image.open(img_file)
img = self.preprocess(img, self.scale)
mask = self.preprocess(mask, self.scale)
item = {"image": torch.from_numpy(img), "mask": torch.from_numpy(mask)}
if self.transforms:
item = self.transforms(item)
return item
from torchvision.models.segmentation.deeplabv3 import DeepLabHead
def create_deeplabv3(num_classes):
model = models.segmentation.deeplabv3_resnet101(pretrained=True, progress=True)
model.classifier = DeepLabHead(2048, num_classes)
#Es sieht gut aus für Graustufen, aber es ist mühsam, also werde ich nicht
#model.backbone.conv1 = nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3, bias=False)
return model
def train_model(model, criterion, optimizer, dataloaders, device, num_epochs=25, print_freq=1):
since = time.time()
best_model_wts = copy.deepcopy(model.state_dict())
best_loss = 1e15
for epoch in range(num_epochs):
print('Epoch {}/{}'.format(epoch+1, num_epochs))
print('-' * 10)
loss_history = {"train": [], "val": []}
for phase in ["train", "val"]:
if phase == "train":
model.train()
else:
model.eval()
for sample in tqdm(iter(dataloaders[phase])):
imgs = sample["image"].to(device, dtype=torch.float)
msks = sample["mask"].to(device, dtype=torch.float)
optimizer.zero_grad()
with torch.set_grad_enabled(phase == "train"):
outputs = model(imgs)
loss = criterion(outputs["out"], msks)
if phase == "train":
loss.backward()
optimizer.step()
epoch_loss = np.float(loss.data)
if (epoch + 1) % print_freq == 0:
print("Epoch: [%d/%d], Loss: %.4f" %(epoch+1, num_epochs, epoch_loss))
loss_history[phase].append(epoch_loss)
# deep copy the model
if phase == "val" and epoch_loss < best_loss:
best_loss = epoch_loss
best_model_wts = copy.deepcopy(model.state_dict())
time_elapsed = time.time() - since
print("Training complete in {:.0f}m {:.0f}s".format(time_elapsed // 60, time_elapsed % 60))
print("Best val Acc: {:4f}".format(best_loss))
model.load_state_dict(best_model_wts)
return model, loss_history
Ausführung lernen
dataset = MaskDataset("images_dir", "masks_dir", 0.5, transforms=None)
#Separat für Schulung und Überprüfung
val_percent= 0.2
batch_size=4
n_val = int(len(dataset) * val_percent)
n_train = len(dataset) - n_val
train, val = random_split(dataset, [n_train, n_val])
train_loader = DataLoader(train, batch_size=batch_size, shuffle=True, num_workers=8, pin_memory=True, drop_last=True )
val_loader = DataLoader(val, batch_size=batch_size, shuffle=False, num_workers=8, pin_memory=True, drop_last=True )
dataloaders = {"train": train_loader, "val": val_loader}
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#Geben Sie bei Verwendung von BCEWithLogitsLoss 1 für die binäre Klassifizierung an
num_classes = 1
model = create_deeplabv3(num_classes)
#Für vorgeübte
#model.load_state_dict(torch.load("model.pth"))
model.to(device)
#Da der Hintergrund überwiegend zahlreich ist, pos_Mit dem Gewicht einstellen
criterion = nn.BCEWithLogitsLoss(pos_weight=torch.tensor(10000.0).to(device))
params = [p for p in model.parameters() if p.requires_grad]
#optimizer = torch.optim.SGD(params, lr=0.005,momentum=0.9, weight_decay=0.0005)
optimizer = optim.Adam(params)
total_epoch = 50
model, loss_dict = train_model(model, criterion, optimizer, dataloaders, device, total_epoch)
Dieses Mal, als ich ungefähr 50 Epochen drehte, konvergierte das Lernen bis zu einem gewissen Grad.
Als Ergebnis war es im Allgemeinen gut und 3 Punkte reagierten richtig, aber gelegentlich wurden auch die folgenden Ergebnisse erhalten (Wärmekartenausdruck).
Natürlich gibt es nie zwei linke Brustwarzen, daher ist der kleine Punkt oben rechts falsch positiv. Übrigens gab es kein falsches Negativ.
Aus dem obigen Inferenzergebnis ergibt die Nachbearbeitung Folgendes:
Schneiden Sie alle Pixel bis auf die Pixel mit klarer Sicherheit für den nächsten Schritt ab. Der Schwellenwert wird diesmal empirisch auf 0,995 festgelegt.
Verwenden Sie cv2.connectedComponents für die Objektpartitionierung (Aufteilung in Cluster). Weitere Informationen finden Sie unter OpenCV - Beschriften verbundener Komponenten mit verbundenen Komponenten --pynote.
Aus der Fallstudie ging hervor, dass der Bereich anderer falsch positiver Ergebnisse als Brustwarze und Nabel klein war. Daher werden wir drei mit einer großen Fläche auswählen. Eigentlich denke ich nicht, dass diese Art von Gegenmaßnahme sehr robust ist, aber diesmal hat es funktioniert, also werde ich es übernehmen.
Verwenden Sie cv2.moments, um den Schwerpunkt jedes Clusters zu ermitteln. Weitere Informationen finden Sie unter Berechnung des Schwerpunkts mit Python + OpenCV - Einführung in die CV-Bildanalyse.
Da die Punkte bei der Konvertierung in affin übereinstimmen müssen, muss die Koordinatenreihenfolge von Brustwarze und Nabel zwischen den Bildern vereinheitlicht werden. Alle Bilder wurden diesmal aufrecht aufgenommen, und es besteht kein Zweifel, dass Brustwarzen → Nabel → Brustwarzen in Richtung der horizontalen Achse erscheinen. Sortieren Sie sie also einfach nach x-Koordinaten.
Zum Zeitpunkt der Folgerung
#3 Punkte von der Maske erkannt
def triangle_pt(heatmask, thresh=0.995):
mask = heatmask.copy()
# 2-4-1.Wenn der Ausgabewert jedes Pixels unter dem Schwellenwert liegt, wird er abgeschnitten.
mask[mask>thresh] = 255
mask[mask<=thresh] = 0
mask = mask.astype(np.uint8)
# 2-4-2.Objekt teilen
nlabels, labels = cv2.connectedComponents(mask)
pt = []
if nlabels != 4:
#Wenn weniger, nichts tun
#Ich möchte die Schwelle wirklich senken, aber es ist ärgerlich
if nlabels < 4:
return None
# 2-4-3.Wenn 4 oder mehr Cluster vorhanden sind, wählen Sie 3 in absteigender Reihenfolge der Fläche aus und verwerfen Sie den Rest
elif nlabels > 4:
sum_px = []
for i in range(1, nlabels):
sum_px.append((labels==i).sum())
#Hintergrund+1
indices = [ x+1 for x in np.argsort(-np.array(sum_px))[:3]]
else:
indices = [x for x in range(1, nlabels)]
# 2-4-4.Finden Sie den Schwerpunkt jedes Clusters
for i in indices:
base = np.zeros_like(mask, dtype=np.uint8)
base[labels==i] = 255
mu = cv2.moments(base, False)
x,y= int(mu["m10"]/mu["m00"]) , int(mu["m01"]/mu["m00"])
pt.append([x,y])
# 2-4-5.Sortieren Sie in aufsteigender x-Koordinate des Schwerpunkts jedes Clusters (rechter Nippel → Nabel → linker Nippel)
sort_key = lambda v: v[0]
pt.sort(key=sort_key)
return np.array(pt)
def correct_img(model, device, in_dir, out_dir,
draw_heatmap=True, draw_triangle=True, correct=True):
imgs = []
base_3p = None
model.eval()
with torch.no_grad():
imglist = sorted(glob.glob(os.path.join(in_dir, "*.jpg ")))
for idx, img_path in enumerate(imglist):
#Es ist ärgerlich, also Losgröße 1
full_img = Image.open(img_path)
img = torch.from_numpy(BasicDataset.preprocess(full_img, 0.5))
img = img.unsqueeze(0)
img = img.to(device=device, dtype=torch.float32)
output = model(img)["out"]
probs = torch.sigmoid(output)
probs = probs.squeeze(0)
tf = transforms.Compose(
[
transforms.ToPILImage(),
transforms.Resize(full_img.size[0]),
transforms.ToTensor()
]
)
probs = tf(probs.cpu())
full_mask = probs.squeeze().cpu().numpy()
full_img = np.asarray(full_img).astype(np.uint8)
full_img = cv2.cvtColor(full_img, cv2.COLOR_RGB2BGR)
#Dreieck
triangle = triangle_pt(full_mask)
if draw_triangle and triangle is not None:
cv2.drawContours(full_img, [triangle], 0, (0, 0, 255), 5)
#Wärmekarte
if draw_heatmap:
full_mask = (full_mask*255).astype(np.uint8)
jet = cv2.applyColorMap(full_mask, cv2.COLORMAP_JET)
alpha = 0.7
full_img = cv2.addWeighted(full_img, alpha, jet, 1 - alpha, 0)
#Affin-Konvertierung
if correct:
if base_3p is None and triangle is not None:
base_3p = triangle
elif triangle is not None:
full_img = p3affine_img(full_img, triangle, base_3p)
if out_dir is not None:
cv2.imwrite(os.path.join(out_dir, os.path.basename(img_path)), full_img)
imgs.append(full_img)
return imgs
imgs = correct_img(model, device,
"images_dir", None,
draw_heatmap=False, draw_triangle=False, correct=True)
Der Zeitraffer kurz vor der Korrektur ist wie folgt.
Der korrigierte Zeitraffer ist wie folgt.
Durch Erkennen der Brustwarze und des Nabels mithilfe von Deep Learning und automatisches Korrigieren des Bildes ist der Zeitraffer erheblich leichter zu erkennen. Das hat mich weiter motiviert zu trainieren. Natürlich denken einige Leute vielleicht ** "Ist das mit einem so nicht tiefen Lebenslauf nicht möglich?" **, aber in meinem Fall würde ich gerne die Langhantel heben, wenn ich Zeit hätte, über die Regeln nachzudenken. Es fühlt sich an, als wäre es mit brutaler Gewalt gelöst worden. Die gesamte Entwicklung wurde mit Google Colab durchgeführt, mit Ausnahme des Koordinatenzuweisungstools 3150 Uu! Die Herausforderung ist
Cortisol wird jedoch ausgeschieden. Machen Sie sich also keine Sorgen, dass es zu schwer ist!
Lass uns ein lustiges Muskeltraining haben!