Kuvatietojen kerääminen ja merkitseminen on yksi resurssiintensiivisimmista tehtävistä kaikissa tietokonenäköprojekteissa. Voi kestää kuukausia kerrallaan kuvavirtojen täydellinen kerääminen, analysointi ja kokeilu sellaisella tasolla, jota tarvitset kilpaillaksesi nykyisellä markkinapaikalla. Senkin jälkeen, kun olet kerännyt tiedot onnistuneesti, sinulla on edelleen jatkuva virta merkintävirheitä, huonosti kehystettyjä kuvia, pieniä määriä merkityksellistä dataa ei-toivottujen kaappausten meressä ja paljon muuta. Näiden suurten pullonkaulojen vuoksi synteettisen tiedon luomisen on oltava jokaisen nykyaikaisen insinöörin työkalupakki. Luomalla 3D-esityksiä kohteista, joita haluamme mallintaa, voimme nopeasti prototyyppiä algoritmeja ja samalla kerätä live-dataa.
Tässä viestissä opastan sinulle esimerkin avoimen lähdekoodin animaatiokirjaston käyttämisestä Blenderistä päästä-päähän synteettisen dataputken rakentamiseen käyttämällä esimerkkinä kananuggetteja. Seuraava kuva havainnollistaa tässä blogikirjoituksessa luotuja tietoja.
Mikä on Blender?
tehosekoitin on avoimen lähdekoodin 3D-grafiikkaohjelmisto, jota käytetään pääasiassa animaatioissa, 3D-tulostuksessa ja virtuaalitodellisuudessa. Siinä on erittäin kattava takila-, animaatio- ja simulaatiosarja, joka mahdollistaa 3D-maailmojen luomisen lähes kaikkiin tietokonenäön käyttötapauksiin. Sillä on myös erittäin aktiivinen tukiyhteisö, jossa useimmat, ellei kaikki, käyttäjän virheet ratkaistaan.
Aseta paikallinen ympäristösi
Asennamme kaksi versiota Blenderistä: toisen paikalliselle koneelle, jolla on pääsy graafiseen käyttöliittymään, ja toisen Amazonin elastinen laskentapilvi (Amazon EC2) P2-esiintymä.
Asenna Blender ja ZPY
Asenna Blender osoitteesta Blenderin verkkosivusto.
Suorita sitten seuraavat vaiheet:
- Suorita seuraavat komennot:
wget https://mirrors.ocf.berkeley.edu/blender/release/Blender3.2/blender-3.2.0-linux-x64.tar.xz
sudo tar -Jxf blender-3.2.0-linux-x64.tar.xz --strip-components=1 -C /bin
rm -rf blender*
/bin/3.2/python/bin/python3.10 -m ensurepip
/bin/3.2/python/bin/python3.10 -m pip install --upgrade pip
- Kopioi tarvittavat Python-otsikot Pythonin Blender-versioon, jotta voit käyttää muita kuin Blender-kirjastoja:
wget https://www.python.org/ftp/python/3.10.2/Python-3.10.2.tgz
tar -xzf Python-3.10.2.tgz
sudo cp Python-3.10.2/Include/* /bin/3.2/python/include/python3.10
- Ohita Blender-versiosi ja pakota asennukset, jotta Blenderin toimittama Python toimii:
/bin/3.2/python/bin/python3.10 -m pip install pybind11 pythran Cython numpy==1.22.1
sudo /bin/3.2/python/bin/python3.10 -m pip install -U Pillow --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U scipy --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U shapely --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U scikit-image --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U gin-config --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U versioneer --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U shapely --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U ptvsd --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U ptvseabornsd --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U zmq --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U pyyaml --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U requests --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U click --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U table-logger --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U tqdm --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U pydash --force
sudo /bin/3.2/python/bin/python3.10 -m pip install -U matplotlib --force
- Lataa
zpy
ja asenna lähteestä:
git clone https://github.com/ZumoLabs/zpy
cd zpy
vi requirements.txt
- Vaihda NumPy-versioksi
>=1.19.4
ja scikit-image>=0.18.1
asennuksen suorittamiseksi 3.10.2
mahdollista, joten et saa ylikirjoituksia:
numpy>=1.19.4
gin-config>=0.3.0
versioneer
scikit-image>=0.18.1
shapely>=1.7.1
ptvsd>=4.3.2
seaborn>=0.11.0
zmq
pyyaml
requests
click
table-logger>=0.3.6
tqdm
pydash
- Varmista yhteensopivuus Blender 3.2:n kanssa siirtymällä kohtaan
zpy/render.py
ja kommentoi seuraavat kaksi riviä (lisätietoja on kohdassa Blender 3.0 -virhe #54):
#scene.render.tile_x = tile_size
#scene.render.tile_y = tile_size
- Asenna seuraavaksi
zpy
kirjasto:
/bin/3.2/python/bin/python3.10 setup.py install --user
/bin/3.2/python/bin/python3.10 -c "import zpy; print(zpy.__version__)"
- Lataa lisäosien versio
zpy
mistä GitHub repo jotta voit käyttää esiintymääsi aktiivisesti:
cd ~
curl -O -L -C - "https://github.com/ZumoLabs/zpy/releases/download/v1.4.1rc9/zpy_addon-v1.4.1rc9.zip"
sudo unzip zpy_addon-v1.4.1rc9.zip -d /bin/3.2/scripts/addons/
mkdir .config/blender/
mkdir .config/blender/3.2
mkdir .config/blender/3.2/scripts
mkdir .config/blender/3.2/scripts/addons/
mkdir .config/blender/3.2/scripts/addons/zpy_addon/
sudo cp -r zpy/zpy_addon/* .config/blender/3.2/scripts/addons/zpy_addon/
- Tallenna tiedosto nimeltä
enable_zpy_addon.py
oman /home
hakemistoon ja suorita käyttöönottokomento, koska sinulla ei ole graafista käyttöliittymää sen aktivoimiseksi:
import bpy, os
p = os.path.abspath('zpy_addon-v1.4.1rc9.zip')
bpy.ops.preferences.addon_install(overwrite=True, filepath=p)
bpy.ops.preferences.addon_enable(module='zpy_addon')
bpy.ops.wm.save_userpref()
sudo blender -b -y --python enable_zpy_addon.py
If zpy-addon
ei asennu (jostain syystä), voit asentaa sen graafisen käyttöliittymän kautta.
- Blenderissä muokata valikosta, valitse Asetukset.
- Valita Lisäosat navigointiruudussa ja aktivoi
zpy
.
Sinun pitäisi nähdä sivu auki GUI:ssa, ja voit valita ZPY. Tämä vahvistaa, että Blender on ladattu.
AliceVision ja Meshroom
Asenna AliceVision ja Meshrooom vastaavista GitHub-varastoista:
FFmpeg
Järjestelmässäsi pitäisi olla ffmpeg
, mutta jos ei, sinun on tehtävä se download se.
Instant Meshes
Voit joko kääntää kirjaston itse tai ladata valmiiksi käännetyt binaarit (joten minä tein) Instant Meshes.
Määritä AWS-ympäristösi
Nyt määritämme AWS-ympäristön EC2-esiintymään. Toistamme edellisen osan vaiheet, mutta vain Blenderille ja zpy
.
- Valitse Amazon EC2 -konsolissa Käynnistä instanssit.
- Valitse AMI. Täältä löytyy muutamia vaihtoehtoja. Voimme joko valita tavallisen Ubuntu-kuvan, valita GPU-esiintymän ja asentaa sitten ohjaimet manuaalisesti ja saada kaikki asetukset, tai voimme valita helpon tien ja aloittaa esikonfiguroidulla Deep Learning AMI:llä ja huolehtia vain Blenderin asentamisesta. käytän toista vaihtoehtoa ja valitsen Deep Learning AMI:n uusimman version Ubuntulle (Deep Learning AMI (Ubuntu 18.04) -versio 61.0).
- varten Ilmentymän tyyppi¸ valitse p2.xlarge.
- Jos sinulla ei ole avainparia, luo uusi tai valitse olemassa oleva.
- Käytä tässä viestissä verkon ja tallennustilan oletusasetuksia.
- Valita Käynnistä instanssit.
- Valita kytkeä ja etsi ohjeet instanssiin kirjautumiseen SSH:sta osoitteessa SSH-asiakas Tab.
- Yhdistä SSH:hen:
ssh -i "your-pem" ubuntu@IPADDRESS.YOUR-REGION.compute.amazonaws.com
Kun olet muodostanut yhteyden ilmentymääsi, noudata samoja asennusvaiheita kuin edellisessä osiossa asentaaksesi Blender ja zpy
.
Tiedonkeruu: Nuggetimme 3D-skannaus
Tätä vaihetta varten käytän iPhonea 360 asteen videon tallentamiseen melko hitaasti nuggetin ympärillä. Kiinnitin kanan nuggetin hammastikkuun ja teippasin hammastikkun työtasolleni ja yksinkertaisesti käänsin kameraani nupin ympäri saadakseni mahdollisimman monta kuvakulmaa. Mitä nopeammin kuvaat, sitä epätodennäköisemmin saat hyviä kuvia käytettäväksi suljinnopeudesta riippuen.
Kun lopetin kuvaamisen, lähetin videon sähköpostiini ja purin videon paikalliselle asemalle. Sieltä käytin ffmepg
leikata video kehyksiksi, mikä helpottaa Meshroomin käyttöä:
mkdir nugget_images
ffmpeg -i VIDEO.mov ffmpeg nugget_images/nugget_%06d.jpg
Avaa Meshroom ja vedä graafinen käyttöliittymä nugget_images
kansio vasemmalla olevaan ruutuun. Valitse sieltä Aloita ja odota muutama tunti (tai vähemmän) riippuen videon pituudesta ja siitä, onko sinulla CUDA-yhteensopiva kone.
Sinun pitäisi nähdä jotain seuraavan kuvakaappauksen kaltaista, kun se on melkein valmis.
Tiedonkeruu: Blenderin käsittely
Kun Meshroom-rekonstruointi on valmis, suorita seuraavat vaiheet:
- Avaa Blenderin käyttöliittymä ja filee valikosta, valitse Tuo, valitse sitten Wavefront (.obj) luomaasi tekstuuritiedostoon Meshroomista.
Tiedosto tulee tallentaa sisään path/to/MeshroomCache/Texturing/uuid-string/texturedMesh.obj
.
- Lataa tiedosto ja tarkkaile hirviötä, joka on 3D-objektisi.
Tässä se menee hieman hankalaksi.
- Vieritä oikeaan yläkulmaan ja valitse rautalanka kuvake Näkymäportin varjostus.
- Valitse objektisi oikeanpuoleisesta näkymästä ja varmista, että se on korostettu, vieritä pääasettelun näyttöporttiin ja paina joko Kieleke tai valitse manuaalisesti Muokkausmoodi.
- Ohjaa seuraavaksi kuvaporttia siten, että voit nähdä objektisi mahdollisimman vähän sen takana. Sinun on tehtävä tämä muutaman kerran saadaksesi sen oikein.
- Napsauta ja vedä rajauslaatikko objektin päälle niin, että vain nugget on korostettuna.
- Kun se on korostettu kuten seuraavassa kuvakaappauksessa, erottelemme hippumme 3D-massasta napsauttamalla hiiren vasenta painiketta ja valitsemalla ErillinenJa sitten Valinta.
Siirrymme nyt oikealle, jossa meidän pitäisi nähdä kaksi teksturoitua objektia: texturedMesh
ja texturedMesh.001
.
- Uuden kohteen pitäisi olla
texturedMesh.001
, joten valitsemme texturedMesh
Ja valitse Poista ei-toivotun massan poistamiseksi.
- Valitse kohde (
texturedMesh.001
) oikealla, siirry katsojaamme ja valitse kohde, Aseta alkuperäja Alkuperä massakeskukseen.
Nyt, jos haluamme, voimme siirtää objektimme katseluportin keskelle (tai yksinkertaisesti jättää sen sinne, missä se on) ja tarkastella sitä kaikessa loistossaan. Huomaa suuri musta aukko, josta emme todellakaan saaneet hyvää elokuvakattavuutta! Meidän on korjattava tämä.
Puhdistaaksemme objektimme pikselien epäpuhtauksista viemme objektimme .obj-tiedostoon. Muista valita Vain valinta viennin aikana.
Tiedonkeruu: Puhdista pikaverkoilla
Meillä on nyt kaksi ongelmaa: kuvassamme on huonosta kuvauksesta johtuva pikseliaukko, joka meidän on siivottava, ja kuvamme on uskomattoman tiheä (mikä tekee kuvien luomisesta erittäin aikaa vievää). Molempien ongelmien ratkaisemiseksi meidän on käytettävä Instant Meshes -nimistä ohjelmistoa ekstrapoloimaan pikselipinta peittämään mustan aukon ja myös kutistamaan koko kohteen pienempään, vähemmän tiheään kokoon.
- Avaa Instant Meshes ja lataa äskettäin tallennetut
nugget.obj
tiedosto.
- Alle Suuntautumiskenttä, valitse Ratkaista.
- Alle Sijaintikenttä, valitse Ratkaista.
Tästä tulee mielenkiintoista. Jos tutkit kohdettasi ja huomaat, että paikanratkaisijan ristikkäiset viivat näyttävät hajaantuneilta, voit valita kampakuvakkeen alta. Suuntautumiskenttä ja piirrä viivat oikein.
- Valita Ratkaista sekä Suuntautumiskenttä ja Sijaintikenttä.
- Jos kaikki näyttää hyvältä, vie verkko ja anna sille jokin nimi
nugget_refined.obj
ja tallenna se levylle.
Tiedonkeruu: Ravista ja paista!
Koska low-poly-verkkoomme ei liity mitään kuvatekstuuria ja high-poly-verkkoomme liittyy, meidän on joko paistettava high-poly-tekstuuri matalan polymeeliinin päälle tai luotava uusi pintakuvio ja määritettävä se meidän kohde. Yksinkertaisuuden vuoksi aiomme luoda kuvatekstuurin tyhjästä ja soveltaa sitä nuggetimme.
Käytin Googlen kuvahakua nuggeteille ja muille paistetuille esineille saadakseni korkearesoluutioisen kuvan paistetun esineen pinnasta. Löysin superkorkean resoluution kuvan paistetusta juustomassasta ja tein uuden kuvan, joka oli täynnä paistettua rakennetta.
Tämän kuvan avulla olen valmis suorittamaan seuraavat vaiheet:
- Avaa Blender ja lataa uusi
nugget_refined.obj
samalla tavalla kuin latasit alkuperäisen objektin: on filee valikosta, valitse Tuo, Wavefront (.obj)ja valitse nugget_refined.obj
tiedosto.
- Siirry seuraavaksi kohtaan Varjostus Tab.
Alareunassa sinun pitäisi huomata kaksi ruutua otsikoineen Periaate BDSF ja Materiaalin lähtö.
- On Lisää valikosta, valitse Rakenne ja Kuvan rakenne.
An Kuvan rakenne laatikon pitäisi ilmestyä.
- Valita Avaa kuva ja lataa paistettu tekstuurikuvasi.
- Vedä hiirtä välillä Väri vuonna Kuvan rakenne laatikko ja Pohjaväri vuonna Periaate BDSF laatikko.
Nyt nuggetin pitäisi olla kunnossa!
Tiedonkeruu: Luo Blender-ympäristömuuttujia
Nyt kun meillä on perusnugget-objekti, meidän on luotava muutamia kokoelmia ja ympäristömuuttujia auttamaan meitä prosessissamme.
- Napsauta hiiren vasemmalla painikkeella käsikohtausaluetta ja valitse Uusi kokoelma.
- Luo seuraavat kokoelmat: TAUSTA, NUGGETja KUUTUNUT.
- Vedä nugget kohtaan NUGGET kokoelma ja nimeä se uudelleen nugget_base.
Tiedonkeruu: Luo kone
Aiomme luoda taustaobjektin, josta meidän nuggetit luodaan, kun renderöimme kuvia. Todellisissa käyttötapauksissa tämä taso on paikka, johon pisteemme sijoitetaan, kuten tarjotin tai roskakori.
- On Lisää valikosta, valitse verkko ja sitten Kone.
Sieltä siirrymme sivun oikealle puolelle ja löydämme oranssin laatikon (Objektin ominaisuudet).
- In Muuttaa ruutu, varten XYZ Euler, asetettu X 46.968: ään, Y 46.968: een ja Z on 1.0.
- Molemmille Sijainti ja Kierto, asetettu X, Yja Z on 0.
Tiedonkeruu: Aseta kamera ja akseli
Seuraavaksi aiomme asettaa kameramme oikein, jotta voimme luoda kuvia.
- On Lisää valikosta, valitse Tyhjä ja Tavallinen akseli.
- Nimeä kohde Pääakseli.
- Varmista, että akselimme on 0 kaikille muuttujille (joten se on suoraan keskellä).
- Jos sinulla on jo kamera, vedä se Pääakselin alle.
- Valita erä ja Muuttaa.
- varten Sijainti, asetettu X 0: ään, Y 0: een ja Z on 100.
Tiedonkeruu: Täältä tulee aurinko
Seuraavaksi lisäämme aurinkoobjektin.
- On Lisää valikosta, valitse Valo ja aurinko.
Tämän objektin sijainnilla ei välttämättä ole väliä, kunhan se on keskitetty jonnekin asettamamme tasoobjektin päälle.
- Valitse vihreä hehkulamppukuvake oikeassa alakulmassa (Objektitietojen ominaisuudet) ja aseta vahvuudeksi 5.0.
- Toista sama toimenpide lisätäksesi a Valo esine ja aseta se satunnaiseen paikkaan koneen päällä.
Tiedonkeruu: Lataa satunnaisia taustoja
Lisäämme kuviisi satunnaisuutta lataamalla niin monta satunnaista tekstuuria osoitteesta rakenne.ninja kuin pystymme (esim. tiilet). Lataa työtilasi kansioon nimeltä random_textures
. Latasin noin 50.
Luo kuvia
Nyt päästään hauskoihin asioihin: kuvien luomiseen.
Kuvanluontiputki: Object3D ja DensityController
Aloitetaan muutamalla koodin määritelmällä:
class Object3D:
'''
object container to store mesh information about the
given object
Returns
the Object3D object
'''
def __init__(self, object: Union[bpy.types.Object, str]):
"""Creates a Object3D object.
Args:
obj (Union[bpy.types.Object, str]): Scene object (or it's name)
"""
self.object = object
self.obj_poly = None
self.mat = None
self.vert = None
self.poly = None
self.bvht = None
self.calc_mat()
self.calc_world_vert()
self.calc_poly()
self.calc_bvht()
def calc_mat(self) -> None:
"""store an instance of the object's matrix_world"""
self.mat = self.object.matrix_world
def calc_world_vert(self) -> None:
"""calculate the verticies from object's matrix_world perspective"""
self.vert = [self.mat @ v.co for v in self.object.data.vertices]
self.obj_poly = np.array(self.vert)
def calc_poly(self) -> None:
"""store an instance of the object's polygons"""
self.poly = [p.vertices for p in self.object.data.polygons]
def calc_bvht(self) -> None:
"""create a BVHTree from the object's polygon"""
self.bvht = BVHTree.FromPolygons( self.vert, self.poly )
def regenerate(self) -> None:
"""reinstantiate the object's variables;
used when the object is manipulated after it's creation"""
self.calc_mat()
self.calc_world_vert()
self.calc_poly()
self.calc_bvht()
def __repr__(self):
return "Object3D: " + self.object.__repr__()
Määrittelemme ensin perussäiliöluokan, jolla on joitain tärkeitä ominaisuuksia. Tämä luokka on pääasiassa olemassa, jotta voimme luoda BVH-puun (tapa esittää nugget-objektiamme 3D-avaruudessa), jossa meidän on käytettävä BVHTree.overlap
menetelmä nähdäksesi, ovatko kaksi itsenäistä luotua nugget-objektia päällekkäisiä 3D-tilassamme. Tästä lisää myöhemmin.
Toinen koodinpala on tiheyssäätimemme. Tämä toimii tapana sitoutua todellisuuden sääntöihin, ei 3D-maailmaan. Esimerkiksi 3D Blender -maailmassa Blenderin objektit voivat olla toistensa sisällä; Kuitenkin, ellei joku tee jotain outoa tiedettä kanankimpaleillemme, haluamme varmistaa, että kaksi nuggettia ei mene päällekkäin siinä määrin, että se tekee siitä visuaalisesti epärealistisen.
Käytämme Plane
objekti synnyttää joukon rajattuja näkymättömiä kuutioita, joista voidaan milloin tahansa kysyä, onko tila varattu vai ei.
Katso seuraava koodi:
class DensityController:
"""Container that controlls the spacial relationship between 3D objects
Returns:
DensityController: The DensityController object.
"""
def __init__(self):
self.bvhtrees = None
self.overlaps = None
self.occupied = None
self.unoccupied = None
self.objects3d = []
def auto_generate_kdtree_cubes(
self,
num_objects: int = 100, # max size of nuggets
) -> None:
"""
function to generate physical kdtree cubes given a plane of -resize- size
this allows us to access each cube's overlap/occupancy status at any given
time
creates a KDTree collection, a cube, a set of individual cubes, and the
BVHTree object for each individual cube
Args:
resize (Tuple[float]): the size of a cube to create XYZ.
cuts (int): how many cuts are made to the cube face
12 cuts == 13 Rows x 13 Columns
"""
Seuraavassa katkelmassa valitsemme nuggetin ja luomme rajaavan kuution sen ympärille. Tämä kuutio edustaa pseudo-kdtree-objektimme yhden pseudovokselin kokoa. Meidän on käytettävä bpy.context.view_layer.update()
funktio, koska kun tätä koodia ajetaan funktion tai komentosarjan sisältä vs. blender-gui, näyttää siltä, että view_layer
ei päivity automaattisesti.
# read the nugget,
# see how large the cube needs to be to encompass a single nugget
# then touch a parameter to allow it to be smaller or larger (eg more touching)
bpy.context.view_layer.objects.active = bpy.context.scene.objects.get('nugget_base')
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS')
#create a cube for the bounding box
bpy.ops.mesh.primitive_cube_add(location=Vector((0,0,0)))
#our new cube is now the active object, so we can keep track of it in a variable:
bound_box = bpy.context.active_object
bound_box.name = 'CUBE1'
bpy.context.view_layer.update()
#copy transforms
nug_dims = bpy.data.objects["nugget_base"].dimensions
bpy.data.objects["CUBE1"].dimensions = nug_dims
bpy.context.view_layer.update()
bpy.data.objects["CUBE1"].location = bpy.data.objects["nugget_base"].location
bpy.context.view_layer.update()
bpy.data.objects["CUBE1"].rotation_euler = bpy.data.objects["nugget_base"].rotation_euler
bpy.context.view_layer.update()
print("bound_box.dimensions: ", bound_box.dimensions)
print("bound_box.location:", bound_box.location)
Seuraavaksi päivitämme hieman kuutioobjektiamme siten, että sen pituus ja leveys ovat neliön muotoisia, toisin kuin sen luodun kimpun luonnollinen koko:
# this cube created isn't always square, but we're going to make it square
# to fit into our
x, y, z = bound_box.dimensions
v = max(x, y)
if np.round(v) < v:
v = np.round(v)+1
bb_x, bb_y = v, v
bound_box.dimensions = Vector((v, v, z))
bpy.context.view_layer.update()
print("bound_box.dimensions updated: ", bound_box.dimensions)
# now we generate a plane
# calc the size of the plane given a max number of boxes.
Nyt käytämme päivitettyä kuutioobjektiamme luodaksemme tason, joka mahtuu tilavuudellisesti num_objects
nuggettien määrä:
x, y, z = bound_box.dimensions
bb_loc = bound_box.location
bb_rot_eu = bound_box.rotation_euler
min_area = (x*y)*num_objects
min_length = min_area / num_objects
print(min_length)
# now we generate a plane
# calc the size of the plane given a max number of boxes.
bpy.ops.mesh.primitive_plane_add(location=Vector((0,0,0)), size = min_length)
plane = bpy.context.selected_objects[0]
plane.name = 'PLANE'
# move our plane to our background collection
# current_collection = plane.users_collection
link_object('PLANE', 'BACKGROUND')
bpy.context.view_layer.update()
Otamme lentokoneemme ja luomme jättiläiskuution, joka on samanpituinen ja leveä kuin lentokoneemme, ja jonka korkeus on nugget-kuutiomme, KUUTI1:
# New Collection
my_coll = bpy.data.collections.new("KDTREE")
# Add collection to scene collection
bpy.context.scene.collection.children.link(my_coll)
# now we generate cubes based on the size of the plane.
bpy.ops.mesh.primitive_cube_add(location=Vector((0,0,0)), size = min_length)
bpy.context.view_layer.update()
cube = bpy.context.selected_objects[0]
cube_dimensions = cube.dimensions
bpy.context.view_layer.update()
cube.dimensions = Vector((cube_dimensions[0], cube_dimensions[1], z))
bpy.context.view_layer.update()
cube.location = bb_loc
bpy.context.view_layer.update()
cube.rotation_euler = bb_rot_eu
bpy.context.view_layer.update()
cube.name = 'cube'
bpy.context.view_layer.update()
current_collection = cube.users_collection
link_object('cube', 'KDTREE')
bpy.context.view_layer.update()
Tästä eteenpäin haluamme luoda vokseleita kuutiostamme. Otamme sopivan määrän kuutioita num_objects
ja leikkaa ne sitten kuutioobjektistamme. Etsimme kuutiomme ylöspäin osoittavaa verkkopinta-alaa ja valitsemme sen sitten leikkauksia varten. Katso seuraava koodi:
# get the bb volume and make the proper cuts to the object
bb_vol = x*y*z
cube_vol = cube_dimensions[0]*cube_dimensions[1]*cube_dimensions[2]
n_cubes = cube_vol / bb_vol
cuts = n_cubes / ((x+y) / 2)
cuts = int(np.round(cuts)) - 1 #
# select the cube
for object in bpy.data.objects:
object.select_set(False)
bpy.context.view_layer.update()
for object in bpy.data.objects:
object.select_set(False)
bpy.data.objects['cube'].select_set(True) # Blender 2.8x
bpy.context.view_layer.objects.active = bpy.context.scene.objects.get('cube')
# set to edit mode
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
print('edit mode success')
# get face_data
context = bpy.context
obj = context.edit_object
me = obj.data
mat = obj.matrix_world
bm = bmesh.from_edit_mesh(me)
up_face = None
# select upwards facing cube-face
# https://blender.stackexchange.com/questions/43067/get-a-face-selected-pointing-upwards
for face in bm.faces:
if (face.normal-UP_VECTOR).length < EPSILON:
up_face = face
break
assert(up_face)
# subdivide the edges to get the perfect kdtree cubes
bmesh.ops.subdivide_edges(bm,
edges=up_face.edges,
use_grid_fill=True,
cuts=cuts)
bpy.context.view_layer.update()
# get the center point of each face
Lopuksi laskemme jokaisen suuresta kuutiostamme tekemämme leikkauksen yläpinnan keskikohdan ja luomme todelliset kuutiot näistä leikkauksista. Jokainen näistä äskettäin luoduista kuutioista edustaa yhtä tilaa, joka synnyttää tai siirtää hippuja koneemme ympäri. Katso seuraava koodi:
face_data = {}
sizes = []
for f, face in enumerate(bm.faces):
face_data[f] = {}
face_data[f]['calc_center_bounds'] = face.calc_center_bounds()
loc = mat @ face_data[f]['calc_center_bounds']
face_data[f]['loc'] = loc
sizes.append(loc[-1])
# get the most common cube-z; we use this to determine the correct loc
counter = Counter()
counter.update(sizes)
most_common = counter.most_common()[0][0]
cube_loc = mat @ cube.location
# get out of edit mode
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
# go to new colection
bvhtrees = {}
for f in face_data:
loc = face_data[f]['loc']
loc = mat @ face_data[f]['calc_center_bounds']
print(loc)
if loc[-1] == most_common:
# set it back down to the floor because the face is elevated to the
# top surface of the cube
loc[-1] = cube_loc[-1]
bpy.ops.mesh.primitive_cube_add(location=loc, size = x)
cube = bpy.context.selected_objects[0]
cube.dimensions = Vector((x, y, z))
# bpy.context.view_layer.update()
cube.name = "cube_{}".format(f)
#my_coll.objects.link(cube)
link_object("cube_{}".format(f), 'KDTREE')
#bpy.context.view_layer.update()
bvhtrees[f] = {
'occupied' : 0,
'object' : Object3D(cube)
}
for object in bpy.data.objects:
object.select_set(False)
bpy.data.objects['CUBE1'].select_set(True) # Blender 2.8x
bpy.ops.object.delete()
return bvhtrees
Seuraavaksi kehitämme algoritmin, joka ymmärtää, mitkä kuutiot ovat varattu kulloinkin, etsii mitkä kohteet menevät päällekkäin ja siirtää päällekkäiset objektit erikseen tyhjään tilaan. Emme pääse eroon kaikista päällekkäisyyksistä kokonaan, mutta voimme saada sen näyttämään riittävän todelliselta.
Katso seuraava koodi:
def find_occupied_space(
self,
objects3d: List[Object3D],
) -> None:
"""
discover which cube's bvhtree is occupied in our kdtree space
Args:
list of Object3D objects
"""
count = 0
occupied = []
for i in self.bvhtrees:
bvhtree = self.bvhtrees[i]['object']
for object3d in objects3d:
if object3d.bvht.overlap(bvhtree.bvht):
self.bvhtrees[i]['occupied'] = 1
def find_overlapping_objects(
self,
objects3d: List[Object3D],
) -> List[Tuple[int]]:
"""
returns which Object3D objects are overlapping
Args:
list of Object3D objects
Returns:
List of indicies from objects3d that are overlap
"""
count = 0
overlaps = []
for i, x_object3d in enumerate(objects3d):
for ii, y_object3d in enumerate(objects3d[i+1:]):
if x_object3d.bvht.overlap(y_object3d.bvht):
overlaps.append((i, ii))
return overlaps
def calc_most_overlapped(
self,
overlaps: List[Tuple[int]]
) -> List[Tuple[int]]:
"""
Algorithm to count the number of edges each index has
and return a sorted list from most->least with the number
of edges each index has.
Args:
list of indicies that are overlapping
Returns:
list of indicies with the total number of overlapps they have
[index, count]
"""
keys = {}
for x,y in overlaps:
if x not in keys:
keys[x] = 0
if y not in keys:
keys[y] = 0
keys[x]+=1
keys[y]+=1
# sort by most edges first
index_counts = sorted(keys.items(), key=lambda x: x[1])[::-1]
return index_counts
def get_random_unoccupied(
self
) -> Union[int,None]:
"""
returns a randomly chosen unoccuped kdtree cube
Return
either the kdtree cube's key or None (meaning all spaces are
currently occupied)
Union[int,None]
"""
unoccupied = []
for i in self.bvhtrees:
if not self.bvhtrees[i]['occupied']:
unoccupied.append(i)
if unoccupied:
random.shuffle(unoccupied)
return unoccupied[0]
else:
return None
def regenerate(
self,
iterable: Union[None, List[Object3D]] = None
) -> None:
"""
this function recalculates each objects world-view information
we default to None, which means we're recalculating the self.bvhtree cubes
Args:
iterable (None or List of Object3D objects). if None, we default to
recalculating the kdtree
"""
if isinstance(iterable, list):
for object in iterable:
object.regenerate()
else:
for idx in self.bvhtrees:
self.bvhtrees[idx]['object'].regenerate()
self.update_tree(idx, occupied=0)
def process_trees_and_objects(
self,
objects3d: List[Object3D],
) -> List[Tuple[int]]:
"""
This function finds all overlapping objects within objects3d,
calculates the objects with the most overlaps, searches within
the kdtree cube space to see which cubes are occupied. It then returns
the edge-counts from the most overlapping objects
Args:
list of Object3D objects
Returns
this returns the output of most_overlapped
"""
overlaps = self.find_overlapping_objects(objects3d)
most_overlapped = self.calc_most_overlapped(overlaps)
self.find_occupied_space(objects3d)
return most_overlapped
def move_objects(
self,
objects3d: List[Object3D],
most_overlapped: List[Tuple[int]],
z_increase_offset: float = 2.,
) -> None:
"""
This function iterates through most-overlapped, and uses
the index to extract the matching object from object3d - it then
finds a random unoccupied kdtree cube and moves the given overlapping
object to that space. It does this for each index from the most-overlapped
function
Args:
objects3d: list of Object3D objects
most_overlapped: a list of tuples (index, count) - where index relates to
where it's found in objects3d and count - how many times it overlaps
with other objects
z_increase_offset: this value increases the Z value of the object in order to
make it appear as though it's off the floor. If you don't augment this value
the object looks like it's 'inside' the ground plane
"""
for idx, cnt in most_overlapped:
object3d = objects3d[idx]
unoccupied_idx = self.get_random_unoccupied()
if unoccupied_idx:
object3d.object.location = self.bvhtrees[unoccupied_idx]['object'].object.location
# ensure the nuggest is above the groundplane
object3d.object.location[-1] = z_increase_offset
self.update_tree(unoccupied_idx, occupied=1)
def dynamic_movement(
self,
objects3d: List[Object3D],
tries: int = 100,
z_offset: float = 2.,
) -> None:
"""
This function resets all objects to get their current positioning
and randomly moves objects around in an attempt to avoid any object
overlaps (we don't want two objects to be spawned in the same position)
Args:
objects3d: list of Object3D objects
tries: int the number of times we want to move objects to random spaces
to ensure no overlaps are present.
z_offset: this value increases the Z value of the object in order to
make it appear as though it's off the floor. If you don't augment this value
the object looks like it's 'inside' the ground plane (see `move_objects`)
"""
# reset all objects
self.regenerate(objects3d)
# regenerate bvhtrees
self.regenerate(None)
most_overlapped = self.process_trees_and_objects(objects3d)
attempts = 0
while most_overlapped:
if attempts>=tries:
break
self.move_objects(objects3d, most_overlapped, z_offset)
attempts+=1
# recalc objects
self.regenerate(objects3d)
# regenerate bvhtrees
self.regenerate(None)
# recalculate overlaps
most_overlapped = self.process_trees_and_objects(objects3d)
def generate_spawn_point(
self,
) -> Vector:
"""
this function generates a random spawn point by finding which
of the kdtree-cubes are unoccupied, and returns one of those
Returns
the Vector location of the kdtree-cube that's unoccupied
"""
idx = self.get_random_unoccupied()
print(idx)
self.update_tree(idx, occupied=1)
return self.bvhtrees[idx]['object'].object.location
def update_tree(
self,
idx: int,
occupied: int,
) -> None:
"""
this function updates the given state (occupied vs. unoccupied) of the
kdtree given the idx
Args:
idx: int
occupied: int
"""
self.bvhtrees[idx]['occupied'] = occupied
Kuvanluontiputki: Hienoja suorituksia
Tässä osiossa erittelemme mitä run
toiminto toimii.
Alustamme omamme DensityController
ja luo jotain, jota kutsutaan säästäjäksi käyttämällä ImageSaver
alkaen zpy
. Tämä antaa meille mahdollisuuden tallentaa renderoidut kuvamme vaivattomasti mihin tahansa valitsemaamme paikkaan. Lisäämme sitten omamme nugget
luokka (ja jos meillä olisi enemmän luokkia, lisäisimme ne tähän). Katso seuraava koodi:
@gin.configurable("run")
@zpy.blender.save_and_revert
def run(
max_num_nuggets: int = 100,
jitter_mesh: bool = True,
jitter_nugget_scale: bool = True,
jitter_material: bool = True,
jitter_nugget_material: bool = False,
number_of_random_materials: int = 50,
nugget_texture_path: str = os.getcwd()+"/nugget_textures",
annotations_path = os.getcwd()+'/nugget_data',
):
"""
Main run function.
"""
density_controller = DensityController()
# Random seed results in unique behavior
zpy.blender.set_seed(random.randint(0,1000000000))
# Create the saver object
saver = zpy.saver_image.ImageSaver(
description="Image of the randomized Amazon nuggets",
output_dir=annotations_path,
)
saver.add_category(name="nugget")
Seuraavaksi meidän on tehtävä lähdeobjekti, josta luodaan kopiohippuja; tässä tapauksessa se on nugget_base
jonka loimme:
# Make a list of source nugget objects
source_nugget_objects = []
for obj in zpy.objects.for_obj_in_collections(
[
bpy.data.collections["NUGGET"],
]
):
assert(obj!=None)
# pass on everything not named nugget
if 'nugget_base' not in obj.name:
print('passing on {}'.format(obj.name))
continue
zpy.objects.segment(obj, name="nugget", as_category=True) #color=nugget_seg_color
print("zpy.objects.segment: check {}".format(obj.name))
source_nugget_objects.append(obj.name)
Nyt kun meillä on perushippu, aiomme tallentaa kaikkien muiden objektien maailmanasennot (paikat), jotta voimme jokaisen renderöintiajon jälkeen käyttää näitä tallennettuja asentoja renderöinnin alustamiseen. Siirrämme myös perushippumme kokonaan pois tieltä, jotta kdtree ei havaitse tilan olevan varattu. Lopuksi alustamme kdtree-cube-objektit. Katso seuraava koodi:
# move nugget point up 10 z's so it won't collide with base-cube
bpy.data.objects["nugget_base"].location[-1] = 10
# Save the position of the camera and light
# create light and camera
zpy.objects.save_pose("Camera")
zpy.objects.save_pose("Sun")
zpy.objects.save_pose("Plane")
zpy.objects.save_pose("Main Axis")
axis = bpy.data.objects['Main Axis']
print('saving poses')
# add some parameters to this
# get the plane-3d object
plane3d = Object3D(bpy.data.objects['Plane'])
# generate kdtree cubes
density_controller.generate_kdtree_cubes()
Seuraava koodi kerää ladatut taustat texture.ninjasta, jossa niitä käytetään satunnaisesti projisoitavaksi koneellemme:
# Pre-create a bunch of random textures
#random_materials = [
# zpy.material.random_texture_mat() for _ in range(number_of_random_materials)
#]
p = os.path.abspath(os.getcwd()+'/random_textures')
print(p)
random_materials = []
for x in os.listdir(p):
texture_path = Path(os.path.join(p,x))
y = zpy.material.make_mat_from_texture(texture_path, name=texture_path.stem)
random_materials.append(y)
#print(random_materials[0])
# Pre-create a bunch of random textures
random_nugget_materials = [
random_nugget_texture_mat(Path(nugget_texture_path)) for _ in range(number_of_random_materials)
]
Tästä taika alkaa. Regeneroimme ensin kdtree-kuutiot tätä ajoa varten, jotta voimme aloittaa alusta:
# Run the sim.
for step_idx in zpy.blender.step():
density_controller.generate_kdtree_cubes()
objects3d = []
num_nuggets = random.randint(40, max_num_nuggets)
log.info(f"Spawning {num_nuggets} nuggets.")
spawned_nugget_objects = []
for _ in range(num_nuggets):
Käytämme tiheyssäädintä luodaksemme satunnaisen syntypisteen nuggetillemme, luomme siitä kopion nugget_base
ja siirrä kopio satunnaisesti luotuun syntypisteeseen:
# Choose location to spawn nuggets
spawn_point = density_controller.generate_spawn_point()
# manually spawn above the floor
# spawn_point[-1] = 1.8 #2.0
# Pick a random object to spawn
_name = random.choice(source_nugget_objects)
log.info(f"Spawning a copy of source nugget {_name} at {spawn_point}")
obj = zpy.objects.copy(
bpy.data.objects[_name],
collection=bpy.data.collections["SPAWNED"],
is_copy=True,
)
obj.location = spawn_point
obj.matrix_world = mathutils.Matrix.Translation(spawn_point)
spawned_nugget_objects.append(obj)
Seuraavaksi värähtelemme satunnaisesti nuggetin kokoa, nuggetin verkkoa ja nuggetin mittakaavaa, jotta kaksi hippua ei näytä samalta:
# Segment the newly spawned nugget as an instance
zpy.objects.segment(obj)
# Jitter final pose of the nugget a little
zpy.objects.jitter(
obj,
rotate_range=(
(0.0, 0.0),
(0.0, 0.0),
(-math.pi * 2, math.pi * 2),
),
)
if jitter_nugget_scale:
# Jitter the scale of each nugget
zpy.objects.jitter(
obj,
scale_range=(
(0.8, 2.0), #1.2
(0.8, 2.0), #1.2
(0.8, 2.0), #1.2
),
)
if jitter_mesh:
# Jitter (deform) the mesh of each nugget
zpy.objects.jitter_mesh(
obj=obj,
scale=(
random.uniform(0.01, 0.03),
random.uniform(0.01, 0.03),
random.uniform(0.01, 0.03),
),
)
if jitter_nugget_material:
# Jitter the material (apperance) of each nugget
for i in range(len(obj.material_slots)):
obj.material_slots[i].material = random.choice(random_nugget_materials)
zpy.material.jitter(obj.material_slots[i].material)
Muutamme nugget-kopiomme a Object3D
objekti, jossa käytämme BVH-puutoimintoa nähdäksemme, leikkaako tasomme tai limittyykö jokin nugget-kopiomme pinta tai kärki. Jos löydämme päällekkäisyyden tason kanssa, siirrämme kimpun yksinkertaisesti ylöspäin sen Z-akselilla. Katso seuraava koodi:
# create 3d obj for movement
nugget3d = Object3D(obj)
# make sure the bottom most part of the nugget is NOT
# inside the plane-object
plane_overlap(plane3d, nugget3d)
objects3d.append(nugget3d)
Nyt kun kaikki nuggetit on luotu, käytämme meidän DensityController
siirtääksesi hippuja ympäriinsä niin, että meillä on minimimäärä päällekkäisyyksiä, ja ne, jotka menevät päällekkäin, eivät näytä kamalalta:
# ensure objects aren't on top of each other
density_controller.dynamic_movement(objects3d)
Seuraavassa koodissa: palautamme Camera
ja Main Axis
poseeraa ja valitse satunnaisesti, kuinka kaukana kamera on Plane
esine:
# Return camera to original position
zpy.objects.restore_pose("Camera")
zpy.objects.restore_pose("Main Axis")
zpy.objects.restore_pose("Camera")
zpy.objects.restore_pose("Main Axis")
# assert these are the correct versions...
assert(bpy.data.objects["Camera"].location == Vector((0,0,100)))
assert(bpy.data.objects["Main Axis"].location == Vector((0,0,0)))
assert(bpy.data.objects["Main Axis"].rotation_euler == Euler((0,0,0)))
# alter the Z ditance with the camera
bpy.data.objects["Camera"].location = (0, 0, random.uniform(0.75, 3.5)*100)
Päätämme kuinka satunnaisesti haluamme kameran kulkevan pitkin Main Axis
. Riippuen siitä, haluammeko sen olevan pääasiassa yläpuolella vai välitämmekö erittäin paljon kulmasta, josta se näkee laudan, voimme säätää top_down_mostly
parametri riippuen siitä, kuinka hyvin harjoitusmallimme poimii signaalin "Mikä edes on nugget?"
# alter the main-axis beta/gamma params
top_down_mostly = False
if top_down_mostly:
zpy.objects.rotate(
bpy.data.objects["Main Axis"],
rotation=(
random.uniform(0.05, 0.05),
random.uniform(0.05, 0.05),
random.uniform(0.05, 0.05),
),
)
else:
zpy.objects.rotate(
bpy.data.objects["Main Axis"],
rotation=(
random.uniform(-1., 1.),
random.uniform(-1., 1.),
random.uniform(-1., 1.),
),
)
print(bpy.data.objects["Main Axis"].rotation_euler)
print(bpy.data.objects["Camera"].location)
Seuraavassa koodissa teemme saman asian kanssa Sun
objekti ja valitse satunnaisesti tekstuuri sille Plane
esine:
# change the background material
# Randomize texture of shelf, floors and walls
for obj in bpy.data.collections["BACKGROUND"].all_objects:
for i in range(len(obj.material_slots)):
# TODO
# Pick one of the random materials
obj.material_slots[i].material = random.choice(random_materials)
if jitter_material:
zpy.material.jitter(obj.material_slots[i].material)
# Sets the material relative to the object
obj.material_slots[i].link = "OBJECT"
# Pick a random hdri (from the local textures folder for background background)
zpy.hdris.random_hdri()
# Return light to original position
zpy.objects.restore_pose("Sun")
# Jitter the light position
zpy.objects.jitter(
"Sun",
translate_range=(
(-5, 5),
(-5, 5),
(-5, 5),
),
)
bpy.data.objects["Sun"].data.energy = random.uniform(0.5, 7)
Lopuksi piilotamme kaikki objektimme, joita emme halua renderöidä: nugget_base
ja koko kuutiorakenne:
# we hide the cube objects
for obj in # we hide the cube objects
for obj in bpy.data.objects:
if 'cube' in obj.name:
obj.hide_render = True
try:
zpy.objects.toggle_hidden(obj, hidden=True)
except:
# deal with this exception here...
pass
# we hide our base nugget object
bpy.data.objects["nugget_base"].hide_render = True
zpy.objects.toggle_hidden(bpy.data.objects["nugget_base"], hidden=True)
Lopuksi käytämme zpy
renderöidäksesi kohtauksemme, tallentaaksesi kuvamme ja tallentaaksesi huomautuksemme. Tätä postausta varten tein joitain pieniä muutoksia zpy
huomautuskirjasto minun erityiseen käyttötapaukseeni (merkintä kuvaa kohti yhden tiedoston sijaan projektia kohden), mutta sinun ei pitäisi olla tätä viestiä varten).
# create the image name
image_uuid = str(uuid.uuid4())
# Name for each of the output images
rgb_image_name = format_image_string(image_uuid, 'rgb')
iseg_image_name = format_image_string(image_uuid, 'iseg')
depth_image_name = format_image_string(image_uuid, 'depth')
zpy.render.render(
rgb_path=saver.output_dir / rgb_image_name,
iseg_path=saver.output_dir / iseg_image_name,
depth_path=saver.output_dir / depth_image_name,
)
# Add images to saver
saver.add_image(
name=rgb_image_name,
style="default",
output_path=saver.output_dir / rgb_image_name,
frame=step_idx,
)
saver.add_image(
name=iseg_image_name,
style="segmentation",
output_path=saver.output_dir / iseg_image_name,
frame=step_idx,
)
saver.add_image(
name=depth_image_name,
style="depth",
output_path=saver.output_dir / depth_image_name,
frame=step_idx,
)
# ideally in this thread, we'll open the anno file
# and write to it directly, saving it after each generation
for obj in spawned_nugget_objects:
# Add annotation to segmentation image
saver.add_annotation(
image=rgb_image_name,
category="nugget",
seg_image=iseg_image_name,
seg_color=tuple(obj.seg.instance_color),
)
# Delete the spawned nuggets
zpy.objects.empty_collection(bpy.data.collections["SPAWNED"])
# Write out annotations
saver.output_annotated_images()
saver.output_meta_analysis()
# # ZUMO Annotations
_output_zumo = _OutputZUMO(saver=saver, annotation_filename = Path(image_uuid + ".zumo.json"))
_output_zumo.output_annotations()
# change the name here..
saver.output_annotated_images()
saver.output_meta_analysis()
# remove the memory of the annotation to free RAM
saver.annotations = []
saver.images = {}
saver.image_name_to_id = {}
saver.seg_annotations_color_to_id = {}
log.info("Simulation complete.")
if __name__ == "__main__":
# Set the logger levels
zpy.logging.set_log_levels("info")
# Parse the gin-config text block
# hack to read a specific gin config
parse_config_from_file('nugget_config.gin')
# Run the sim
run()
Voila!
Suorita päätön luomisskripti
Nyt kun meillä on tallennettu Blender-tiedosto, luotu nugget ja kaikki tukitiedot, pakataanpa työhakemistomme ja joko scp
sen GPU-koneellemme tai ladannut sen kautta Amazonin yksinkertainen tallennuspalvelu (Amazon S3) tai muu palvelu:
tar cvf working_blender_dir.tar.gz working_blender_dir
scp -i "your.pem" working_blender_dir.tar.gz ubuntu@EC2-INSTANCE.compute.amazonaws.com:/home/ubuntu/working_blender_dir.tar.gz
Kirjaudu sisään EC2-instanssiisi ja pura working_blender-kansiosi:
tar xvf working_blender_dir.tar.gz
Nyt luomme tietomme kaikessa loistossaan:
blender working_blender_dir/nugget.blend --background --python working_blender_dir/create_synthetic_nuggets.py
Komentosarjan tulee suorittaa 500 kuvalle, ja tiedot tallennetaan /path/to/working_blender_dir/nugget_data
.
Seuraava koodi näyttää yksittäisen tietojoukollamme luodun huomautuksen:
{
"metadata": {
"description": "3D data of a nugget!",
"contributor": "Matt Krzus",
"url": "krzum@amazon.com",
"year": "2021",
"date_created": "20210924_000000",
"save_path": "/home/ubuntu/working_blender_dir/nugget_data"
},
"categories": {
"0": {
"name": "nugget",
"supercategories": [],
"subcategories": [],
"color": [
0.0,
0.0,
0.0
],
"count": 6700,
"subcategory_count": [],
"id": 0
}
},
"images": {
"0": {
"name": "a0bb1fd3-c2ec-403c-aacf-07e0c07f4fdd.rgb.png",
"style": "default",
"output_path": "/home/ubuntu/working_blender_dir/nugget_data/a0bb1fd3-c2ec-403c-aacf-07e0c07f4fdd.rgb.png",
"relative_path": "a0bb1fd3-c2ec-403c-aacf-07e0c07f4fdd.rgb.png",
"frame": 97,
"width": 640,
"height": 480,
"id": 0
},
"1": {
"name": "a0bb1fd3-c2ec-403c-aacf-07e0c07f4fdd.iseg.png",
"style": "segmentation",
"output_path": "/home/ubuntu/working_blender_dir/nugget_data/a0bb1fd3-c2ec-403c-aacf-07e0c07f4fdd.iseg.png",
"relative_path": "a0bb1fd3-c2ec-403c-aacf-07e0c07f4fdd.iseg.png",
"frame": 97,
"width": 640,
"height": 480,
"id": 1
},
"2": {
"name": "a0bb1fd3-c2ec-403c-aacf-07e0c07f4fdd.depth.png",
"style": "depth",
"output_path": "/home/ubuntu/working_blender_dir/nugget_data/a0bb1fd3-c2ec-403c-aacf-07e0c07f4fdd.depth.png",
"relative_path": "a0bb1fd3-c2ec-403c-aacf-07e0c07f4fdd.depth.png",
"frame": 97,
"width": 640,
"height": 480,
"id": 2
}
},
"annotations": [
{
"image_id": 0,
"category_id": 0,
"id": 0,
"seg_color": [
1.0,
0.6000000238418579,
0.9333333373069763
],
"color": [
1.0,
0.6,
0.9333333333333333
],
"segmentation": [
[
299.0,
308.99,
292.0,
308.99,
283.01,
301.0,
286.01,
297.0,
285.01,
294.0,
288.01,
285.0,
283.01,
275.0,
287.0,
271.01,
294.0,
271.01,
302.99,
280.0,
305.99,
286.0,
305.99,
303.0,
302.0,
307.99,
299.0,
308.99
]
],
"bbox": [
283.01,
271.01,
22.980000000000018,
37.98000000000002
],
"area": 667.0802000000008,
"bboxes": [
[
283.01,
271.01,
22.980000000000018,
37.98000000000002
]
],
"areas": [
667.0802000000008
]
},
{
"image_id": 0,
"category_id": 0,
"id": 1,
"seg_color": [
1.0,
0.4000000059604645,
1.0
],
"color": [
1.0,
0.4,
1.0
],
"segmentation": [
[
241.0,
273.99,
236.0,
271.99,
234.0,
273.99,
230.01,
270.0,
232.01,
268.0,
231.01,
263.0,
233.01,
261.0,
229.0,
257.99,
225.0,
257.99,
223.01,
255.0,
225.01,
253.0,
227.01,
246.0,
235.0,
239.01,
238.0,
239.01,
240.0,
237.01,
247.0,
237.01,
252.99,
245.0,
253.99,
252.0,
246.99,
269.0,
241.0,
273.99
]
],
"bbox": [
223.01,
237.01,
30.980000000000018,
36.98000000000002
],
"area": 743.5502000000008,
"bboxes": [
[
223.01,
237.01,
30.980000000000018,
36.98000000000002
]
],
"areas": [
743.5502000000008
]
},
...
...
...
Yhteenveto
Tässä viestissä osoitin, kuinka avoimen lähdekoodin animaatiokirjaston Blenderin avulla voidaan rakentaa päästä päähän synteettinen dataputki.
Blenderissä ja AWS:ssä voit tehdä paljon hienoja asioita; toivottavasti tämä demo voi auttaa sinua seuraavassa datan nälkäisessä projektissasi!
Viitteet
kirjailijasta
Matt Krzus on vanhempi datatutkija Amazon Web Servicessä AWS Professional Services -ryhmässä