def computePlateArtifacts(channelName, imageArr, bucket, plateId, embeddingName): flatFieldKey = "artifact/plate/" + plateId + "/" + embeddingName + "/channel-" + channelName + "-flatfield.npy" if bi.s3ObjectExists(bucket, flatFieldKey): print("FlatfieldKey already exists, skipping bucket={} key={}".format(bucket, flatFieldKey)) return else: print("FlatfieldKey does not exist, computing bucket={} key={}".format(bucket, flatFieldKey)) plateImgArr=[] for imageBucket, imageKey in imageArr: print("Loading bucket={} key={}".format(imageBucket, imageKey)) imageObject = s3c.get_object(Bucket=imageBucket, Key=imageKey) file_stream = imageObject['Body'] im = Image.open(file_stream) pix = np.array(im) plateImgArr.append(pix) print("Computing flat field image") npAllImages=np.array(plateImgArr).astype(np.float32) npAllImages = npAllImages / 65536.0 s1 = npAllImages.shape s2 = s1[1:] npAvg = np.zeros(shape=s2, dtype=np.float32); for ni in range(npAllImages.shape[0]): npAvg = npAvg+(npAllImages[ni]/(npAllImages.shape[0])) h1 = histogram(npAvg, 100) pcut = bi.findHistCutoff(h1, 0.30) bi.applyImageCutoff(npAvg, pcut) g1 = gaussian(npAvg, 50) image_type = flatFieldKey[-3:] tmpDir = bi.getTmpDir() fn = tmpDir + '/' + su.uuid() + '.' + image_type if image_type.lower() == 'npy': with open(fn, 'wb') as f: np.save(f, g1) with open(fn, 'rb') as fdata: print("s3c.upload_fileobj: bucket={} flatFieldKey={}".format(bucket, flatFieldKey)) s3c.upload_fileobj(fdata, bucket, flatFieldKey) else: max1=np.max(g1) min1=np.min(g1) g1 = (g1-min1)/(max1-min1) img=Image.fromarray(g1) img.save(fn) with open(fn, 'rb') as fdata: s3c.upload_fileobj(fdata, bucket, flatFieldKey) artifactClient = bioims.client('artifact') artifactKey = "s3key#" + flatFieldKey artifact = { "contextId" : args.plateId, "trainId" : "origin", "artifact" : artifactKey } artifactClient.createArtifact(artifact) fnPath = Path(fn) fnPath.unlink() tPath = Path(tmpDir) tPath.rmdir()
import sys import json import boto3 sys.path.insert(0, "../src") import bioims tagClient = bioims.client('tag') # for i in range(10): # tag = 'testTag_{}'.format(i) # r = tagClient.createTag(tag) # print(r) # for i in range(10): # tag = 'testTag_{}'.format(i) # r = tagClient.getTagByValue(tag) # print(r) # for i in range(10): # tag = 'testTag_{}'.format(i) # r = tagClient.getTagById(i) # print(r) r = tagClient.getAllTags() print(r)
parser = argparse.ArgumentParser() parser.add_argument('--region', type=str, help='AWS region') parser.add_argument('--bucket', type=str, help='Bucket name for artifacts') parser.add_argument('--plateId', type=str, help='plateId') parser.add_argument('--embeddingName', type=str, help='embeddingName') args = parser.parse_args() print("region={} bucket={} plateId={} embeddingName={}".format(args.region, args.bucket, args.plateId, args.embeddingName)) os.environ['AWS_DEFAULT_REGION'] = args.region s3c = boto3.client('s3') imageManagementClient = bioims.client('image-management') imlist = imageManagementClient.getImagesByPlateId(args.plateId) channelImages = {} for image in imlist: item = image['Item'] imageBucket = item['bucket'] imageKeyPrefix = item['key'] channelKeys = item['channelKeys'] for channel in channelKeys: name = channel['name'] keysuffix = channel['keysuffix'] fullpath = (imageBucket, imageKeyPrefix + keysuffix) if name in channelImages:
import sys import json import boto3 sys.path.insert(0, "../src") import bioims trainClient = bioims.client('train') embeddingName='bbbc021' filterBucket='' filterKey='' executeProcessPlate='true' useSpot='false' r = trainClient.train(embeddingName, filterBucket, filterKey, executeProcessPlate, useSpot) print(r)
def handler(event, context): trainId = event['trainId'] useSpotArg = event['useSpot'] useSpot = True if useSpotArg.lower() == 'false': useSpot = False uniqueId = su.uuid() trainingConfigurationClient = bioims.client('training-configuration') trainInfo = trainingConfigurationClient.getTraining(trainId) embeddingName = trainInfo['embeddingName'] embeddingInfo = trainingConfigurationClient.getEmbeddingInfo(embeddingName) trainScriptBucket = embeddingInfo['modelTrainingScriptBucket'] trainScriptKey = embeddingInfo['modelTrainingScriptKey'] localTrainingScript = '/tmp/bioims-training-script.py' getS3TextObjectWriteToPath(trainScriptBucket, trainScriptKey, localTrainingScript) trainListArtifactKey = bp.getTrainImageListArtifactPath(trainId) sagemaker_session = sagemaker.Session() sagemaker_bucket = sagemaker_session.default_bucket() sagemaker_role = sagemaker.get_execution_role() py_version = '1.6.0' instance_type = embeddingInfo['trainingInstanceType'] trainingHyperparameters = embeddingInfo['trainingHyperparameters'] fsxInfo = getFsxInfo() print(fsxInfo) directory_path = '/' + fsxInfo['mountName'] sgIds = [] sgIds.append(fsxInfo['securityGroup']) jobName = 'bioims-' + trainId + '-' + uniqueId checkpoint_s3_uri = "s3://" + sagemaker_bucket + "/checkpoints/" + jobName file_system_input = FileSystemInput(file_system_id=fsxInfo['fsxId'], file_system_type='FSxLustre', directory_path=directory_path, file_system_access_mode='ro') trainingHyperparameters['train_list_file'] = trainListArtifactKey if useSpot: estimator = PyTorch( entry_point=localTrainingScript, role=sagemaker_role, framework_version=py_version, instance_count=1, instance_type=instance_type, py_version='py36', image_name= '763104351884.dkr.ecr.us-east-1.amazonaws.com/pytorch-training:1.6.0-gpu-py36-cu101-ubuntu16.04', subnets=fsxInfo['subnetIds'], security_group_ids=sgIds, hyperparameters=trainingHyperparameters, train_use_spot_instances=True, train_max_wait=100000, train_max_run=100000, checkpoint_s3_uri=checkpoint_s3_uri, debugger_hook_config=False) else: estimator = PyTorch( entry_point=localTrainingScript, role=sagemaker_role, framework_version=py_version, instance_count=1, instance_type=instance_type, py_version='py36', image_name= '763104351884.dkr.ecr.us-east-1.amazonaws.com/pytorch-training:1.6.0-gpu-py36-cu101-ubuntu16.04', subnets=fsxInfo['subnetIds'], security_group_ids=sgIds, hyperparameters=trainingHyperparameters, train_use_spot_instances=False, checkpoint_s3_uri=checkpoint_s3_uri, debugger_hook_config=False) trainingConfigurationClient.updateTraining(trainId, 'sagemakerJobName', jobName) estimator.fit(file_system_input, wait=False, job_name=jobName) responseInfo = {'trainingJobName': jobName} response = {'statusCode': 200, 'body': responseInfo} return response
s3c = boto3.client('s3') parser = argparse.ArgumentParser() parser.add_argument('--plate', type=str, help='bbbc-021 test plate') parser.add_argument('--extension', type=str, help='either tif or npy') args = parser.parse_args() PYTHON = 'python3.8' PROJECT_TOP_DIR = '/home/ec2-user/environment/bioimage-search' BBBC_021_BUCKET = 'bioimagesearchbbbc021stack-bbbc021bucket544c3e64-10ecnwo51127' TEST_BUCKET = 'bioimagesearchbasestack-bioimagesearchtestbucket3-djdwcbvul5zb' testId = shortuuid.uuid() testPlateName = args.plate platePreprocessingClient = bioims.client('plate-preprocessing') testDir = '/home/ec2-user/tmp/' + testId os.system('mkdir -p ' + testDir) generateImageListScript = PROJECT_TOP_DIR + '/datasets/bbbc-021/scripts/list_plate_image_keys_for_channel.py' channelList = [ 'dapi', 'tubulin', 'actin'] for channel in channelList: imageFilePrefix = testPlateName + '-' + channel + '-flatfield' imageListFilename = imageFilePrefix + '.txt' imageListLocalFile = testDir + '/' + imageListFilename cmdList = [] cmdList.append(PYTHON) cmdList.append(generateImageListScript) cmdList.append('--bucket') cmdList.append(BBBC_021_BUCKET)
import sys import json from random import seed from random import randint import boto3 sys.path.insert(0, "../src") import bioims messageClient = bioims.client('message') seed(1) mids=[] for i in range(10): r = messageClient.createMessage("message test "+str(i)) mids.append(r) for i in range(10): r = messageClient.getMessage(mids[i]) print(r) for i in range(10): print(i) print(mids[i]) r = messageClient.addMessage(mids[i], "message test add3 "+str(i)) print(r) for i in range(10): print(i) print(mids[i])
import sys import json from random import seed from random import randint import boto3 sys.path.insert(0, "../src") import bioims artifactClient = bioims.client('artifact') # print(artifactClient.getLambdaArn()) # seed(1) # mids=[] # for i in range(10): # artifact = { # "contextId" : 'plate-'+str(i), # "trainId" : 'train-id-'+str(i), # "artifact" : 's3key#'+str(i), # "s3bucket" : 's3bucket-'+str(i), # "description" : 'type-'+str(i) # } # r = artifactClient.createArtifact(artifact) # print(r) # for i in range(10): # r = artifactClient.getArtifacts('plate-'+str(i), 'train-id-'+str(i)) # print(r)
response = table.scan(IndexName=PLATE_INDEX) items = response['Items'] while 'LastEvaluatedKey' in response: response = table.scan(IndexName=PLATE_INDEX, ExclusiveStartKey=response['LastEvaluatedKey']) items.extend(response['Items']) itemsLength = len(items) print("Found {} plate:image rows".format(itemsLength)) for imageItem in items: allImageIds.append(imageItem['imageId']) # Step 2: Get all valid trainIds - note this includes 'origin' trainingConfigurationClient = bioims.client('training-configuration') trainingsToKeep = {} for embedding in embeddingsToKeep: trainList = trainingConfigurationClient.getEmbeddingTrainings('bbbc021') for training in trainList: trainId = training['trainId'] trainingsToKeep[trainId] = True trainCount = len(trainingsToKeep) print("Found {} trainIds".format(trainCount)) # Step 3: iterate through allImageIds[] and delete rows # corresponding to trainIds not on the keep list. totalRowsDeleted = 0 for i, imageId in enumerate(allImageIds):
import sys import json from random import seed from random import randint import boto3 sys.path.insert(0, "../src") import bioims labelClient = bioims.client('label') # seed(1) # labelClient.createCategory("category2", "description1") # for i in range(10): # r = labelClient.createLabel("category2", "label"+str(i)) # print(r) # r = labelClient.listLabels("category2") # print(r) # r = labelClient.updateCategoryDescription("category2", "description3") # print(r) # r = labelClient.listCategories() # print(r) # r = labelClient.getIndex("category2", "label9") # print(r) # r = labelClient.deleteCategory("category2")
import sys import json import boto3 import io sys.path.insert(0, "../src") import bioims TEST_INPUT_BUCKET = "bioimage-search-input" TEST_OUTPUT_BUCKET = "bioimage-search-output" trainingConfigurationClient = bioims.client('training-configuration') imageManagementClient = bioims.client('image-management') # NOTE: 'origin' is not actually a valid trainId # training = { # "train_id": "origin", # "filterBucket": "bioimage-search-input", # "filterIncludeKey": "", # "filterExcludeKey": "", # "embeddingName": "embedding-name1", # "sagemakerTrainId": "sagemaker-train-id1", # "trainMessageId": "train-message-id1", # "modelBucket" : "model-bucket1", # "modelKey" : "model-key1" # } # SourcePlateInfo { # plateSourceId: <string> # images: [ # wellSourceId: <string>
import sys import json import boto3 sys.path.insert(0, "../src") import bioims searchClient = bioims.client('search') r = searchClient.loadTagLabelMap(); print(r) r = searchClient.startTagLoad('bbbc021') print(r) # trainId = 'hqTvRAmUVR5amUiAABqv85' # imageId = 'bgzxxYiiuKEYawB7wEX8pW' # r = searchClient.searchByImageId(trainId, imageId) # print(r)
import sys import json import boto3 sys.path.insert(0, "../src") import bioims import numpy as np import base64 searchClient = bioims.client('search') trainingConfigurationClient = bioims.client('training-configuration') trainList = trainingConfigurationClient.getEmbeddingTrainings('bbbc021') # This will log the current loaded trainId list to the log of the search servcie, which might be useful: searchClient.logTrainList() for training in trainList: trainId = training['trainId'] if trainId != 'origin': print(trainId) searchClient.deleteTraining(trainId) # Examples for deleting specific trainIds: # searchClient.deleteTraining('7g9h5c2cS8HjAs4kdcDdRK') # searchClient.deleteTraining('7jFcXLieHNyRTRyQ5gaDRA')
Steps: 1. Read in the info for the trainId and its embedding 2. Read in the filter list for the trainId, if there is a list 3. Create a list to accumulate train file prefix paths 4. Get the list of compatible plateIds for the Embedding associated the trainId 5. For each plateId, get its corresponding list of imageIds 6. Filter out imageIds in the exclusion list 7. For included imageIds, generate and add paths to the train file prefix list 8. Once the entire list is generated, write to artifact path and add entries in both the train and artifact tables """ s3c = boto3.client('s3') imageClient = bioims.client('image-management') stacksDescription = imageClient.getStacksDescription() params = {"stacksDescription": stacksDescription} trainConfigurationClient = bioims.client('training-configuration', params) artifactClient = bioims.client('artifact', params) def getCompatiblePlates(embeddingInfo): width = embeddingInfo['inputWidth'] height = embeddingInfo['inputHeight'] depth = embeddingInfo['inputDepth'] channels = embeddingInfo['inputChannels'] request = '{{ "method": "listCompatiblePlates", "width": {}, "height": {}, "depth": {}, "channels": {}}}'.format( width, height, depth, channels) payload = bytes(request, encoding='utf-8') lambdaClient = boto3.client('lambda')
import sys import json import boto3 sys.path.insert(0, "../src") import bioims imageArtifactClient = bioims.client('image-artifact') #{ # "input_bucket": "bioimagesearchbbbc021stack-bbbc021bucket544c3e64-1t2bv8cktyrtq", # "input_keys": [ # "Week10_40111/Week10_200907_B02_s1_w18E215662-2CF7-4739-93F3-DBD0C40B78DB.tif", # "Week10_40111/Week10_200907_B02_s1_w2D492FCE4-15C2-4C66-99A5-E2235A93A3CC.tif", # "Week10_40111/Week10_200907_B02_s1_w436D0A3BC-098D-4271-B5AA-361CA0A7DC88.tif" # ], # "output_bucket": "bioimage-search-output", # "medium_artifact_key": "test-medium.png", # "thumbnail_artifact_key": "test-thumbnail.png" #} input_key_list = [ "Week10_40111/Week10_200907_B02_s1_w18E215662-2CF7-4739-93F3-DBD0C40B78DB.tif", "Week10_40111/Week10_200907_B02_s1_w2D492FCE4-15C2-4C66-99A5-E2235A93A3CC.tif", "Week10_40111/Week10_200907_B02_s1_w436D0A3BC-098D-4271-B5AA-361CA0A7DC88.tif" ] artifact_keys = ["test-medium.png", "test-thumbnail.png"] artifact_sizes = [1000, 200] r = imageArtifactClient.generateDefaultArtifacts(
sourceCompoundMap[imageSourceId] = compound bbbc021ImageCount = len(image_df.index) print("BBBC-021 image count={}".format(bbbc021ImageCount)) #imagesRemovedByCompound={} moaDict = {} i = 0 for k, v in compound_moa_map.items(): print("i={} key={} value={}".format(i, k, v)) moaDict[v] = True # removedList = [] # imagesRemovedByCompound[k]=removedList i += 1 imageClient = bioims.client('image-management') trainingConfigurationClient = bioims.client('training-configuration') tagClient = bioims.client('tag') embeddingInfo = trainingConfigurationClient.getEmbeddingInfo(EMBEDDING) print(embeddingInfo) width = embeddingInfo['inputWidth'] height = embeddingInfo['inputHeight'] depth = embeddingInfo['inputDepth'] channels = embeddingInfo['inputChannels'] print("list compatible plates: width={} height={} depth={} channels={}".format( width, height, depth, channels)) plateList = imageClient.listCompatiblePlates(width, height, depth, channels) pl = len(plateList) print("found {} compatible plates".format(pl))
import sys import json import boto3 import io sys.path.insert(0, "../src") import bioims INPUT_BUCKET = 'bioimage-search-input' INPUT_KEY_1 = 'sourcePlateInfo_Week1_22141.json' INPUT_KEY_2 = 'sourcePlateInfo_Week1_22123.json' INPUT_KEY_3 = 'sourcePlateInfo_Week1_22401.json' processPlateClient = bioims.client('process-plate') r1 = processPlateClient.uploadSourcePlate(INPUT_BUCKET, INPUT_KEY_1) print(r1) # r2 = processPlateClient.uploadSourcePlate(INPUT_BUCKET, INPUT_KEY_2) # print(r2) # r3 = processPlateClient.uploadSourcePlate(INPUT_BUCKET, INPUT_KEY_3) # print(r3)
import sys sys.path.insert(0, "../src") import bioims configurationClient = bioims.client('configuration') configurationClient.setParameter("default-image-artifact-sizes", "100,1000") configurationClient.setParameter("default-image-artifact-keys", "thumbnail-2d.png,medium-2d.png") configurationClient.setParameter("image-preprocessing-roi-size", "128") configurationClient.setParameter("image-preprocessing-min-voxels", "200")
import bioims # Inputs #trainId = "r6KEudzQCuUtDwCzziiMZT" #imageId = "17Sk8AHeX1idyJDBnMwEhX" # trainingConfigurationClient = bioims.client('training-configuration') # stacksDescription = trainingConfigurationClient.getStacksDescription() # params = { # 'stacksDescription': stacksDescription # } # imageManagementClient = bioims.client('image-management', params) #embeddingClient = bioims.client('embedding', params) embeddingClient = bioims.client('embedding') # trainInfo = trainingConfigurationClient.getTraining(trainId) # embeddingName = trainInfo['embeddingName'] # embeddingInfo = trainingConfigurationClient.getEmbeddingInfo(embeddingName) # imageOriginItem = imageManagementClient.getImageInfo(imageId, 'origin') # imageOrigin = imageOriginItem['Item'] # plateId = imageOrigin['plateId'] # print(trainInfo) # print(embeddingName) # print(embeddingInfo) # print(imageOrigin) # print(plateId) #r = embeddingClient.executeImageEmbeddingCompute(trainInfo, embeddingInfo, plateId, imageId)
def handler(event, context): s3c = boto3.client('s3') imageId = event['imageId'] describeStacks = event['describeStacks']['Payload']['body'] contextId = describeStacks['contextId'] trainId = describeStacks['trainId'] key = describeStacks['key'] print("imageId={}".format(imageId)) print("contextId={}".format(contextId)) print("trainId={}".format(trainId)) print("key={}".format(key)) print("dataBucket={}".format(dataBucket)) ###################################### describeStacksParams = {"bucket": dataBucket, "key": key} imageManagementClient = bioims.client('image-management', describeStacksParams) imageInfo = imageManagementClient.getImageInfo(imageId, "origin") configurationClient = bioims.client('configuration', describeStacksParams) artifactClient = bioims.client('artifact', describeStacksParams) sizesStr = configurationClient.getParameter(CONFIG_SIZES_PARAM) artifact_sizes = sizesStr.split(',') keysStr = configurationClient.getParameter(CONFIG_KEYS_PARAM) artifact_keys = keysStr.split(',') # artifact/plate/<plate>/default/image/<imageId>/<key>.tif # {'Item': # { 'trainCategory': 'moa', # 'imageId': 'ecG7rUcJL2asM4AvoEmom9', # 'plateId': 'bWb5wnbxsPPUyTVhfjV8Wh', # 'trainId': 'origin', # 'depth': '1', # 'plateSourceId': 'Week1_22401', # 'bucket': 'bioimagesearchbbbc021stack-bbbc021bucket544c3e64-10ecnwo51127', # 'experiment': 'BBBC021_v1', # 'channelKeys': [ # {'name': 'dapi', # 'keysuffix': 'Week1_22401/Week1_150607_G11_s4_w1FD01EB31-90F9-4856-AD6B-B5160E2C5BA3.tif'}, # {'name': 'tubulin', # 'keysuffix': 'Week1_22401/Week1_150607_G11_s4_w2E853D1E6-2637-488D-829C-D6AB6AD8A2E2.tif'}, # {'name': 'actin', 'keysuffix': 'Week1_22401/Week1_150607_G11_s4_w4768D7DAA-05D3-48FA-9223-954979D8C802.tif'} # ], # 'wellId': 'amTSiU11M5knT9DKvgtmm5', # 'imageSourceId': 'Week1_150607_G11_s4_w1FD01EB31-90F9-4856-AD6B-B5160E2C5BA3', # 'messageId': '0e366bc9-ab55-4eeb-9d79-9f661e3ce712', # 'searchReady': 'VALIDATED', # 'height': '1024', # 'width': '1280', # 'wellSourceId': 'G11', # 'channels': '3', # 'trainLabel': 'DMSO', # 'key': '', # 'createTimestamp': '1608160666210' # } # } ###################################### plateId = imageInfo['Item']['plateId'] input_bucket = imageInfo['Item']['bucket'] input_keys = [] keyPrefix = imageInfo['Item']['key'] channelKeys = imageInfo['Item']['channelKeys'] for channel in channelKeys: fullKey = keyPrefix + channel['keysuffix'] input_keys.append(fullKey) artifact_key_prefix = "artifact/plate/{}/default/image/{}/".format( plateId, imageId) input_data = [] if len(input_keys) == 0: return { 'success': "False", 'errorMsg': "one or more input keys required" } if len(artifact_keys) == 0: return { 'success': "False", 'errorMsg': "one or more artifact_keys required" } if len(artifact_sizes) != len(artifact_keys): return { 'success': "False", 'errorMsg': "each artifact_key must have corresponding artifact_size" } elif len(input_keys) == 1: input_key = input_keys[0] fileObject = s3c.get_object(Bucket=input_bucket, Key=input_key) file_stream = fileObject['Body'] im = Image.open(file_stream) input_data = np.array(im) if len(input_data.shape) == 2: input_data = np.expand_dims(input_data, axis=0) else: input_arr = [] input_shape = [] for input_key in input_keys: retries = 3 while retries > 0: try: print("Loading {} {} remaining tries {}".format( input_bucket, input_key, retries)) fileObject = s3c.get_object(Bucket=input_bucket, Key=input_key) file_stream = fileObject['Body'] im = Image.open(file_stream) break except: time.sleep(1) retries -= 1 if retries == 0: raise Exception( "Ran out of retries for accessing {} {}".format( input_bucket, input_key)) pix = np.array(im) input_shape.append(pix.shape) input_arr.append(pix) if not bi.checkPixShape(input_shape): return { 'success': "False", 'errorMsg': "input channel dimensions do not match" } input_data = np.array(input_arr) print("input_data shape=", input_data.shape) input_data = bi.normImageData(input_data) bavgFill = np.zeros(shape=input_data[0].shape, dtype=input_data.dtype) for c in range(input_data.shape[0]): channelData = input_data[c] h1 = histogram(channelData, 100) bcut = bi.findHistCutoff(h1, 0.20) bavg = bi.findCutoffAvg(channelData, bcut) bavgFill.fill(bavg) bi.normalizeChannel(bavgFill, channelData) ca = bi.getColors(input_data.shape[0]) mip = bi.calcMip(input_data, ca) img = Image.fromarray(mip) height = input_data.shape[-2] width = input_data.shape[-1] for artifact_key, artifact_size in zip(artifact_keys, artifact_sizes): artifactFullKey = artifact_key_prefix + artifact_key image_type = artifact_key[-3:] asize = float(artifact_size) if height > width: artifact_height = int(asize) artifact_width = int((width / height) * artifact_height) else: artifact_width = int(asize) artifact_height = int((height / width) * artifact_width) artifact_img = img.resize((artifact_width, artifact_height)) artifact_buffer = BytesIO() artifact_img.save(artifact_buffer, format=image_type) artifact_buffer.seek(0) s3c.upload_fileobj(artifact_buffer, dataBucket, artifactFullKey) artifactTableKey = "s3key#" + artifactFullKey artifact = { "contextId": imageId, "trainId": "origin", "artifact": artifactTableKey } artifactClient.createArtifact(artifact) return imageId