Neuralt nätverksdrivet planetarium med Python, Electron och Keras: 8 steg
Neuralt nätverksdrivet planetarium med Python, Electron och Keras: 8 steg
Anonim
Neuralt nätverksdrivet planetarium med hjälp av Python, Electron och Keras
Neuralt nätverksdrivet planetarium med hjälp av Python, Electron och Keras

I denna instruerbara, ska jag visa dig hur jag skrev en automatisk 3D planetarium generator, med hjälp av Python och elektron

Videon ovan visar ett av de slumpmässiga planetarier som programmet genererade.

** Obs! Det här programmet är inte på något sätt perfekt och på vissa ställen inte särskilt pytoniskt. Den neurala nätdiskrimineraren är bara ~ 89% korrekt, så några udda bilder kommer att ta sig in i planetariet **

Specifika

Planetariet frågar efter ett NASA-API för rymdrelaterade bilder och använder ett konvolutionellt neuralt nätverk för att avgöra om bilden är lämplig för bearbetning. Programmet använder sedan OpenCV för att ta bort bakgrunden från bilden, och slutligen sys bilderna ihop till en stor ekvektangulär bild. Den här bilden sparas sedan och ett Electron Node.js -program öppnar bilden och använder PhotoSphere.js -paketet för att visa bilden i ett 3D -format i planetariumstil.

Beroenden

Pytonorm:

  • Keras
  • Kudde
  • cv2
  • Knasig
  • Förfrågningar
  • urllib
  • Slumpmässig
  • tid
  • io

Elektron:

PhotoSphere

Steg 1: Konfigurera din miljö

Installera elektron och python

Kontrollera först att du har node.js och npm installerat (om inte kan du ladda ner här)

Därefter måste du installera Electron. Öppna en kommandotolk och ange följande kommando:

npm installera elektron -g

Därefter behöver du python, som kan laddas ner här

Skapa en virtuell miljö

Öppna en kommandotolk och ange följande kommandon för att konfigurera din virtuella miljö:

pip installera virtualenv

virtuellt nätutrymme

cd -utrymme

skript / aktivera

Installera Python -beroenden

Kör dessa kommandon i kommandotolken för att installera dina pythonberoenden:

pip installera keras

pip installera kudden

pip installera numpy

pip -installationsförfrågningar

pip installera opencv-pythonOm du vill träna nätverket själv, se till att ställa in GPU -acceleration för Keras

Steg 2: Fråga NASA Search API

Översikt

NASA har många riktigt användbara API: er som du kan använda med dina projekt. För detta projekt kommer vi att använda sök-API: et, vilket gör att vi kan söka efter NASA: s bilddatabas efter rymdrelaterade bilder.

Koden

Först måste vi definiera en python -funktion för att acceptera ett argument som kommer att fungera som sökterm:

def get_image_search (fras):

passera

Därefter kommer vi att konvertera söktermen till URL -format och sedan använda förfrågningsbiblioteket för att fråga API:

def get_image_search (fras):

params = {"q": urllib.parse.quote (arg), "media_type": "image"} results = requests.get ("https://images-api.nasa.gov/search", params = params)

Slutligen kommer vi att avkoda samlingen+JSON -strängen som API: et returnerade till oss och extrahera en lista med länkar till bilder relaterade till söktermen:

def get_image_search (fras):

params = {"q": urllib.parse.quote (arg), "media_type": "image"} results = requests.get ("https://images-api.nasa.gov/search", params = params) data = [resultat ['href'] för resultat i results.json () ["samling"] ["objekt"]

Där går vi! Vi har nu ett kodavsnitt som kan fråga NASA -bildsöknings -API: et och returnera en lista med länkar till bilder relaterade till vår sökterm.

Steg 3: Det konvolutionella neurala nätverket

Översikt

Det neurala nätverkets uppgift är att klassificera om en bild är av något i rymden, eller om det inte är det. För att göra detta kommer vi att använda ett konvolutionellt neuralt nätverk, eller CNN, för att utföra en serie matrisoperationer på bilden och bestämma hur rymd-y det är. Jag kommer inte att förklara allt detta, eftersom det finns mycket teori bakom det, men om du vill lära dig om neurala nätverk föreslår jag "Machine Learning Mastery"

Koden

Först måste vi importera våra beroenden:

importera os

#Fix för problem under tåg steg på GPU os.environ ['CUDA_VISIBLE_DEVICES'] = '' import tensorflow som tf if tf.test.gpu_device_name (): print ('GPU found') else: print ("No GPU found") från keras.preprocessing.image import ImageDataGenerator från keras.preprocessing import image from keras.models import Sequential from keras.layers import Conv2D, MaxPooling2D from keras.layers import Activation, Dropout, Flatten, Dense from keras import backend as K from PIL import Image importera numpy som np

Därefter måste vi definiera vår modell:

img_width, img_height = 1000, 500

train_data_dir = 'v_data/train' validation_data_dir = 'v_data/test' nb_train_samples = 203 nb_validation_samples = 203 epochs = 10 batch_size = 8 if K.image_data_format () == 'channel_first': input_shape = (3, im_w_ = = (img_width, img_height, 3) model = Sequential () model.add (Conv2D (32, (2, 2), input_shape = input_shape)) model.add (Activation ('relu')) model.add (MaxPooling2D (pool_size) = (2, 2))) model.add (Conv2D (32, (2, 2))) model.add (Activation ('relu')) model.add (MaxPooling2D (pool_size = (2, 2))) model.add (Conv2D (64, (2, 2))) model.add (Activation ('relu')) model.add (MaxPooling2D (pool_size = (2, 2))) model.add (Flatten ()) model. lägg till (Dense (64)) model.add (Activation ('relu')) model.add (Dropout (0.5)) model.add (Dense (1)) model.add (Activation ('sigmoid')) model.compile (loss = 'binary_crossentropy', optimizer = 'rmsprop', metrics = ['precision'])

Jag har tränat modellen åt dig, men om du skulle vilja träna modellen själv, på din egen dataset, så har jag bifogat utbildningskoden. Annars kan du ladda ner den utbildade modellens HDF5 -fil. På grund av Instructables -filbegränsningar har jag fått byta namn på den med ett ".txt" -tillägg. För att använda den, byt namn på filen till ett ".h5" -tillägg och ladda den med den här koden:

model.load_weights ("model_saved.h5")

För att använda nätverket för att förutsäga hur rymd-y en bild är kommer vi att definiera denna funktion:

def förutsäga (image_path):

img = image.load_img (image_path, target_size = (1000, 500)) img = np.expand_dims (img, axis = 0) result = model.predict_classes (img) return result [0] [0]

Steg 4: Bearbeta bilden

Översikt

För bildbehandling använder jag OpenCV (cv2) biblioteket. Först suddar vi ut kanterna på bilden och sedan tar vi bort bakgrunden genom att skapa en mask och ändra alfa -värdena för de mörkare färgerna

Koden

Detta är den del av funktionen som suddar kanterna:

def processImage (img):

RADIUS = 20 # Öppna en bild im = Image.open ("pilbuffer.png") # Klistra in bilden på vit bakgrund diam = 2 * RADIUS back = Image.new ('RGB', (im.size [0] + diam, im.size [1] + diam), (0, 0, 0)) back.paste (im, (RADIUS, RADIUS)) # Skapa oskärpa mask mask = Image.new ('L', (im.size [0] + diam, im.size [1] + diam), 255) blck = Image.new ('L', (im.size [0] - diam, im.size [1] - diam), 0) mask. klistra in (blck, (diam, diam)) # Oskärpa bild och klistra in suddig kant enligt mask suddighet = baksida.filter (ImageFilter. GaussianBlur (RADIUS / 2)) baksida. klistra in (oskärpa, mask = mask) tillbaka. spara (" transition.png ") back.close ()

Därefter ställer vi in de mörkare färgerna på transparent och sparar bilden tillfälligt:

#Skapa mask och filter ersätt svart med alfa

image = cv2.imread ("övergång.png") hMin = 0 sMin = 0 vMin = 20 hMax = 180 sMax = 255 vMax = 255 lägre = np.array ([hMin, sMin, vMin]) övre = np.array ([hMax, sMax, vMax]) hsv = cv2.cvtColor (bild, cv2. COLOR_BGR2HSV) mask = cv2.inRange (hsv, nedre, övre) output = cv2.bitwise_and (bild, bild, mask = mask) *_, alfa = cv2.split (output) dst = cv2.merge ((output, alpha)) output = dst with open ("buffer.png", "w+") as file: pass cv2.imwrite ("buffer.png", output)

Steg 5: Sy ihop bilder i en ekvektangulär projektion

Översikt

Denna funktion tar flera bilder och syr dem i ett format som kan tolkas av PhotoSphere.js -paketet med hjälp av PIL (kudde) biblioteket

Koden

Först måste vi skapa en bild som kan fungera som värd för de andra bilderna:

new = Image.new ("RGBA", (8000, 4000), color = (0, 0, 0))

Därefter måste vi iterera genom bildmängden (som alla har ändrats till 1000x500) och placerat dem i bilden:

h = 0

w = 0 i = 0 för img i img_arr: new.paste (img, (w, h), img) w += 1000 om w == 8000: h += 500 w = 0 i += 1

Nu packar vi upp det här i en funktion som tar en rad bilder som argument och returnerar den nya bilden:

def stitch_beta (img_arr):

new = Image.new ("RGBA", (8000, 4000), color = (0, 0, 0)) h = 0 w = 0 i = 0 för img i img_arr: new.paste (img, (w, h), img) w += 1000 om w == 8000: h += 500 w = 0 i += 1 returnerar nytt

Steg 6: Full Python Script

Detta är det fullständiga python -neurala nätverksskriptet, som sparas som net.py och importeras till huvudskriptet:

# importera bibliotek

import os #Fix för problem under tåg steg på GPU os.environ ['CUDA_VISIBLE_DEVICES'] = '' importera tensorflow som tf if tf.test.gpu_device_name (): print ('GPU found') else: print ("No GPU found ") från keras.preprocessing.image import ImageDataGenerator från keras.preprocessing import image från keras.models import Sequential från keras.layers import Conv2D, MaxPooling2D från keras.layers import Aktivering, Dropout, Flatten, Tät från keras import backend som K från PIL import Image import numpy as np img_width, img_height = 1000, 500 train_data_dir = 'v_data/train' validation_data_dir = 'v_data/test' nb_train_samples = 203 nb_validation_samples = 203 epochs = 10 batch_size = 8 if K.imat_data ': input_shape = (3, img_width, img_height) else: input_shape = (img_width, img_height, 3) model = Sequential () model.add (Conv2D (32, (2, 2), input_shape = input_shape)) model.add (Activation ('relu')) model.add (MaxPooling2D (pool_size = (2, 2))) model.add (Conv2D (32, (2, 2)))) modell. lägg till (Activation ('relu')) model.add (MaxPooling2D (pool_size = (2, 2))) model.add (Conv2D (64, (2, 2))) model.add (Activation ('relu')) model.add (MaxPooling2D (pool_size = (2, 2))) model.add (Flatten ()) model.add (Dense (64)) model.add (Activation ('relu')) model.add (Dropout (0.5)) model.add (Dense (1)) model.add (Activation ('sigmoid')) model.compile (loss = 'binary_crossentropy', optimizer = 'rmsprop', metrics = ['precision']) model.load_weights ("model_saved.h5") def predict (image_path): img = image.load_img (image_path, target_size = (1000, 500)) img = np.expand_dims (img, axis = 0) result = model.predict_classes (img) return result [0] [0]

Detta är huvudpythonfilen, api.py:

importförfrågningar, sys, random, urllib.parse, cv2

från PIL import Image, ImageFilter från io import BytesIO import numpy som np import net def get_image_search (num, fras): count = 0 img_arr = för arg i fras: print (arg) print (f "Aktuellt bildantal: {count } ") i = 0 params = {" q ": urllib.parse.quote (arg)," media_type ":" image "} results = requests.get (" https://images-api.nasa.gov/search ", params = params) data = [result ['href'] för resultat i results.json () [" collection "] [" items "] print (len (data)) if num> len (data): num = len (data) medan räkna = num: break print (f "\ n {count} images retreived") return img_arr def stitch_beta (img_arr): new = Image.new ("RGBA", (8000, 4000), color = (0, 0, 0)) h = 0 w = 0 i = 0 för img i img_arr: #pbar.set_description (f "Bearbetar bild {i +1}") new.paste (img, (w, h), img) w += 1000 if w == 8000: h += 500 w = 0 i += 1 returnera ny def processImage (img): RADIUS = 20 # Öppna en bild im = Image.open ("pilbuffer.png") # Klistra in bild på vit bakgrund diam = 2 * RADIUS tillbaka = Image.new ('RGB', (im.size [0] + diam, im.size [1] + diam), (0, 0, 0)) back.paste (im, (RADIUS, RADIUS)) # Skapa oskärpa maskmask = Image.new ('L', (im.size [0] + diam, im.size [1] + diam), 255) blck = Image.new ('L', (im.size [0] - diam, im.size [1] - diam), 0) mask.paste (blck, (diam, diam)) # Oskärpa bild och klistra in suddig kant enligt mask suddighet = back.filter (ImageFilter. GaussianBlur (RADIUS / 2)) back.paste (oskärpa, mask = mask) back.save ("övergång.png") tillbaka.slut () #Skapa mask och filter ersätt svart med alfa image = cv2.imread (" genomresa ion.png ") hMin = 0 sMin = 0 vMin = 20 hMax = 180 sMax = 255 vMax = 255 lägre = np.array ([hMin, sMin, vMin]) övre = np.array ([hMax, sMax, vMax]) hsv = cv2.cvtColor (bild, cv2. COLOR_BGR2HSV) mask = cv2.inRange (hsv, nedre, övre) output = cv2.bitwise_and (bild, bild, mask = mask) *_, alfa = cv2.split (output) dst = cv2.merge ((output, alpha)) output = dst with open ("buffer.png", "w+") as file: pass cv2.imwrite ("buffer.png", output) #Edge detection och suddighet om _name_ == "_main_": search_terms = ["supernova", "planet", "galax", "Vintergatan", "nebulosa", "stjärnor"] #Söktermerna kan ändras till vad du vill att planetariet ska inkludera img_arr = get_image_search (64, search_terms) print ("Bilder hämtade och neuralfiltrerade") img = stitch_beta (img_arr) print ("Images stitched") img.save ("stitched.png")

Steg 7: Elektronappen

Översikt

Vi kommer att skapa en enkel elektronapp som bara placerar och laddar PhotoSphere -elementet. Main.js- och package.json -filerna är direkt från Electrons webbplats och HTML är en något modifierad version av HTML -koden som finns på PhotoSphere -webbplatsen. Jag har inkluderat filerna, men bytt namn på alla till.txt, eftersom Instructables inte tillåter dessa filtyper. För att använda filerna, byt namn på dem med lämpligt tillägg.

Koden

main.js

const {app, BrowserWindow} = require ('elektron')

function createWindow () {const win = new BrowserWindow ({width: 800, height: 600, webPreferences: {nodeIntegration: true}}) win.loadFile ('index.html')} app.whenReady (). sedan (createWindow) app.on ('window-all-closed', () => {if (process.platform! == 'darwin') {app.quit ()}}) app.on ('active', () => {if (BrowserWindow.getAllWindows (). length === 0) {createWindow ()}})

package.json

{

"name": "space", "version": "0.1.0", "main": "main.js", "scripts": {"start": "electron." }}

index.html

Steg 8: Utförande

Skapa den ekvektangulära bilden

För att skapa bilden, kör api.py -skriptet i kommandotolken, med dess virtuella miljö aktiverad:

api.py

När skripten har körts klart kör du elektronappen med:

npm startVoila! Ditt planetarium är aktivt! Tack för att du läser:)

Rekommenderad: