This repository has been archived by the owner on Oct 31, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 284
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use different Docker image for blender verification (#3844)
* Use different Docker image for blender verification * test cleanup * test failing docker * Update scripts to newer version * Fix to pixel shifts * Run docker tests on linux only * freeze scikit-learn
- Loading branch information
Showing
33 changed files
with
1,678 additions
and
1,217 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
FROM golemfactory/blender:1.8 | ||
|
||
# Install scripts requirements first, then add scripts. | ||
ADD scripts_verifier/requirements.txt /golem/work/ | ||
ADD scripts_verifier/copy.sh /golem/ | ||
|
||
# Install any needed packages specified in requirements.txt | ||
RUN set +x \ | ||
&& apt-get update \ | ||
&& apt-get install -y libglib2.0-0 \ | ||
&& apt-get install -y g++ \ | ||
&& apt-get install -y libsm6 \ | ||
&& apt-get install -y libxrender1 \ | ||
&& apt-get install -y wget \ | ||
&& apt-get install -y zlib1g-dev \ | ||
&& apt-get install -y libopenexr-dev \ | ||
&& /golem/install_py_libs.sh /golem/work/requirements.txt \ | ||
&& /golem/copy.sh \ | ||
&& apt-get remove -y libopenexr-dev \ | ||
&& apt-get remove -y zlib1g-dev \ | ||
&& apt-get remove -y wget \ | ||
&& apt-get remove -y libxrender1 \ | ||
&& apt-get remove -y libsm6 \ | ||
&& apt-get remove -y g++ \ | ||
&& apt-get remove -y libglib2.0-0 \ | ||
&& apt-get clean \ | ||
&& apt-get -y autoremove \ | ||
&& rm -rf /var/lib/apt/lists/* | ||
|
||
ENV PYTHONPATH=/golem/scripts:/golem/scripts_verifier:/golem:$PYTHONPATH | ||
|
||
# Create symbolic link to python. I don't know where, something removes it. | ||
RUN ln -s /usr/bin/python3.6 /usr/bin/python3 | ||
|
||
RUN mkdir /golem/scripts_verifier | ||
ADD scripts_verifier/ /golem/scripts_verifier |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
cp /usr/lib/x86_64-linux-gnu/libXrender.so.1 /usr/local/lib/. | ||
cp /usr/lib/x86_64-linux-gnu/libSM.so.6 /usr/local/lib/. | ||
cp /usr/lib/x86_64-linux-gnu/libgthread-2.0.so.0 /usr/local/lib/. | ||
cp /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0 /usr/local/lib/. | ||
cp /usr/lib/x86_64-linux-gnu/libICE.so.6 /usr/local/lib/. | ||
cp /usr/lib/x86_64-linux-gnu/libIex-2_2.so.12 /usr/local/lib/. | ||
cp /usr/lib/x86_64-linux-gnu/libIlmImf-2_2.so.22 /usr/local/lib/. | ||
cp /usr/lib/x86_64-linux-gnu/libHalf.so.12 /usr/local/lib/. | ||
cp /usr/lib/x86_64-linux-gnu/libIlmThread-2_2.so.12 /usr/local/lib/. |
173 changes: 173 additions & 0 deletions
173
apps/blender/resources/images/scripts_verifier/crop_generator.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
import os | ||
import numpy | ||
import math | ||
import random | ||
from typing import Dict, Tuple, List, Optional | ||
|
||
WORK_DIR = "/golem/work" | ||
OUTPUT_DIR = "/golem/output" | ||
|
||
class Region: | ||
|
||
def __init__(self, left: float, top: float, right: float, bottom: float): | ||
self.left = left | ||
self.right = right | ||
self.top = top | ||
self.bottom = bottom | ||
|
||
class PixelRegion: | ||
|
||
def __init__(self, left: int, top: int, right: int, bottom: int): | ||
self.left = left | ||
self.right = right | ||
self.top = top | ||
self.bottom = bottom | ||
|
||
class SubImage: | ||
|
||
CROP_RELATIVE_SIZE = 0.1 | ||
PIXEL_OFFSET = numpy.float32(0.5) | ||
MIN_CROP_SIZE = 8 | ||
|
||
def __init__(self, region: Region, resolution: List[int]): | ||
self.region = region | ||
self.pixel_region = self.calculate_pixels(region, resolution[0], resolution[1]) | ||
self.width = self.pixel_region.right - self.pixel_region.left | ||
self.height = self.pixel_region.top - self.pixel_region.bottom | ||
self.resolution = resolution | ||
|
||
def calculate_pixels(self, region: Region, width: int, height: int) -> None: | ||
# This is how Blender is calculating pixel, check | ||
# BlenderSync::get_buffer_params in blender_camera.cpp file | ||
# BoundBox2D border = cam->border.clamp(); | ||
# params.full_x = (int)(border.left * (float)width); | ||
|
||
# NOTE blender uses floats (single precision) while python operates on | ||
# doubles | ||
# Here numpy is used to emulate this loss of precision when assigning | ||
# double to float: | ||
left = math.floor( | ||
numpy.float32(region.left) * numpy.float32(width) + | ||
SubImage.PIXEL_OFFSET) | ||
|
||
right = math.floor( | ||
numpy.float32(region.right) * numpy.float32(width) + | ||
SubImage.PIXEL_OFFSET) | ||
|
||
# NOTE we are exchanging here top with bottom, because borders | ||
# in blender are in OpenGL UV coordinate system (left, bottom is 0,0) | ||
# where pixel values are for use in classic coordinate system (left, top is 0,0) | ||
|
||
top = math.floor( | ||
numpy.float32(region.bottom) * numpy.float32(height) + | ||
SubImage.PIXEL_OFFSET) | ||
|
||
bottom = math.floor( | ||
numpy.float32(region.top) * numpy.float32(height) + | ||
SubImage.PIXEL_OFFSET) | ||
|
||
print("Pixels left=%r, top=%r, right=%r, bottom=%r" % | ||
(left, top, right, bottom)) | ||
return PixelRegion(left, top, right, bottom) | ||
|
||
@staticmethod | ||
def __calculate_crop_side_length(subtask_side_length: int) -> int: | ||
calculated_length = int( | ||
SubImage.CROP_RELATIVE_SIZE * subtask_side_length) | ||
|
||
return max(SubImage.MIN_CROP_SIZE, calculated_length) | ||
|
||
def get_default_crop_size(self) -> Tuple[int, int]: | ||
x = self.__calculate_crop_side_length(self.width) | ||
y = self.__calculate_crop_side_length(self.height) | ||
return x, y | ||
|
||
class Crop: | ||
|
||
@staticmethod | ||
def create_from_region(id: int, crop_region: Region, subimage: SubImage): | ||
crop = Crop(id, subimage) | ||
crop.crop_region = crop_region | ||
crop.pixel_region = crop.subimage.calculate_pixels(crop_region, | ||
subimage.width, subimage.height) | ||
return crop | ||
|
||
@staticmethod | ||
def create_from_pixel_region(id: int, pixel_region: PixelRegion, subimage: SubImage): | ||
crop = Crop(id, subimage) | ||
crop.pixel_region = pixel_region | ||
crop.crop_region = crop.calculate_borders(pixel_region, subimage.resolution[0], subimage.resolution[1]) | ||
return crop | ||
|
||
def __init__(self, id: int, subimage: SubImage): | ||
self.id = id | ||
self.subimage = subimage | ||
self.pixel_region = None | ||
self.crop_region = None | ||
|
||
def get_relative_top_left(self) \ | ||
-> Tuple[int, int]: | ||
# get top left corner of crop in relation to particular subimage | ||
print("Sumimag top=%r - crop.top=%r" % (self.subimage.region.top, self.pixel_region.top)) | ||
y = self.subimage.pixel_region.top - self.pixel_region.top | ||
print("X=%r, Y=%r" % (self.pixel_region.left, y)) | ||
return self.pixel_region.left, y | ||
|
||
def calculate_borders(self, pixel_region: PixelRegion, width: int, height: int): | ||
|
||
left = numpy.float32( | ||
(numpy.float32(pixel_region.left) + SubImage.PIXEL_OFFSET) / | ||
numpy.float32(width)) | ||
|
||
right = numpy.float32( | ||
(numpy.float32(pixel_region.right) + SubImage.PIXEL_OFFSET) / | ||
numpy.float32(width)) | ||
|
||
bottom = numpy.float32( | ||
(numpy.float32(pixel_region.top) + SubImage.PIXEL_OFFSET) / | ||
numpy.float32(height)) | ||
|
||
top = numpy.float32( | ||
(numpy.float32(pixel_region.bottom) + SubImage.PIXEL_OFFSET) / | ||
numpy.float32(height)) | ||
|
||
return Region(left, top, right, bottom) | ||
|
||
def generate_single_random_crop_data(subimage: SubImage, crop_size_px: Tuple[int, int], id: int) \ | ||
-> Crop: | ||
|
||
crop_horizontal_pixel_coordinates = _get_random_interval_within_boundaries( | ||
subimage.pixel_region.left, | ||
subimage.pixel_region.right, | ||
crop_size_px[0]) | ||
|
||
crop_vertical_pixel_coordinates = _get_random_interval_within_boundaries( | ||
subimage.pixel_region.bottom, | ||
subimage.pixel_region.top, | ||
crop_size_px[1]) | ||
|
||
crop = Crop.create_from_pixel_region(id, PixelRegion( | ||
crop_horizontal_pixel_coordinates[0], | ||
crop_vertical_pixel_coordinates[1], | ||
crop_horizontal_pixel_coordinates[1], | ||
crop_vertical_pixel_coordinates[0]), subimage) | ||
|
||
return crop | ||
|
||
def _get_random_interval_within_boundaries(begin: int, | ||
end: int, | ||
interval_length: int) \ | ||
-> Tuple[int, int]: | ||
|
||
# survive in edge cases | ||
end -= 1 | ||
begin += 1 | ||
|
||
print("begin %r, end %r" % (begin, end)) | ||
|
||
max_possible_interval_end = (end - interval_length) | ||
if max_possible_interval_end < 0: | ||
raise Exception("Subtask is too small for reliable verification") | ||
interval_begin = random.randint(begin, max_possible_interval_end) | ||
interval_end = interval_begin + interval_length | ||
return interval_begin, interval_end |
40 changes: 40 additions & 0 deletions
40
apps/blender/resources/images/scripts_verifier/decision_tree.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import numpy | ||
from sklearn.externals import joblib | ||
|
||
|
||
## ======================= ## | ||
## | ||
class DecisionTree: | ||
|
||
## ======================= ## | ||
## | ||
def __init__( self, clf ): | ||
self.clf = clf | ||
|
||
## ======================= ## | ||
## | ||
@staticmethod | ||
def load( file ): | ||
data = joblib.load( file ) | ||
tree = DecisionTree( data[0] ) | ||
|
||
return tree, data[1] | ||
|
||
## ======================= ## | ||
## | ||
def classify_with_feature_vector(self, feature_vector, labels): | ||
|
||
numpy_format = [] | ||
for label in labels: | ||
numpy_format.append((label, numpy.float64)) | ||
|
||
converted_features = numpy.zeros(1, dtype=numpy_format) | ||
for name in converted_features.dtype.names: | ||
converted_features[name] = feature_vector[name] | ||
|
||
samples = converted_features.view(numpy.float64).reshape( | ||
converted_features.shape + (-1,)) | ||
|
||
results = self.clf.predict(samples) | ||
|
||
return numpy.array(results) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from PIL import Image, ImageFilter | ||
import numpy | ||
from skimage import compare_mse | ||
|
||
|
||
import sys | ||
|
||
|
||
## ======================= ## | ||
## | ||
class MetricEdgeFactor: | ||
|
||
|
||
## ======================= ## | ||
## | ||
@staticmethod | ||
def compute_metrics( image1, image2 ): | ||
|
||
image1 = image1.convert("RGB") | ||
image2 = image2.convert("RGB") | ||
|
||
edged_image1 = image1.filter( ImageFilter.FIND_EDGES ) | ||
edged_image2 = image2.filter( ImageFilter.FIND_EDGES ) | ||
|
||
np_image1 = numpy.array( edged_image1 ) | ||
np_image2 = numpy.array( edged_image2 ) | ||
|
||
ref_edge_factor = numpy.mean( np_image1 ) | ||
comp_edge_factor = numpy.mean( np_image2 ) | ||
|
||
edge_factor = compare_mse( np_image1, np_image2 ) | ||
|
||
result = dict() | ||
result[ "ref_edge_factor" ] = ref_edge_factor | ||
result[ "comp_edge_factor" ] = comp_edge_factor | ||
result[ "edge_difference" ] = edge_factor | ||
|
||
return result | ||
|
||
## ======================= ## | ||
## | ||
@staticmethod | ||
def get_labels(): | ||
return [ "ref_edge_factor", "comp_edge_factor", "edge_difference" ] | ||
|
||
## ======================= ## | ||
## | ||
def run(): | ||
|
||
first_img = sys.argv[ 1 ] | ||
second_img = sys.argv[ 2 ] | ||
|
||
first_img = Image.open( first_img ) | ||
second_img = Image.open( second_img ) | ||
|
||
ssim = MetricEdgeFactor() | ||
|
||
print( ssim.compute_metrics( first_img, second_img ) ) | ||
|
||
|
||
|
||
if __name__ == "__main__": | ||
run() |
56 changes: 56 additions & 0 deletions
56
apps/blender/resources/images/scripts_verifier/histograms_correlation.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import cv2 | ||
import numpy | ||
from PIL import Image | ||
import sys | ||
|
||
|
||
class MetricHistogramsCorrelation: | ||
|
||
@staticmethod | ||
def compute_metrics( image1, image2): | ||
if image1.size != image2.size: | ||
raise Exception("Image sizes differ") | ||
opencv_image_1 = cv2.cvtColor(numpy.array(image1), cv2.COLOR_RGB2BGR) | ||
opencv_image_2 = cv2.cvtColor(numpy.array(image2), cv2.COLOR_RGB2BGR) | ||
return {"histograms_correlation": MetricHistogramsCorrelation.compare_histograms(opencv_image_1, opencv_image_2)} | ||
|
||
@staticmethod | ||
def get_labels(): | ||
return ["histograms_correlation"] | ||
|
||
@staticmethod | ||
def get_number_of_pixels(image): | ||
height, width = image.shape[:2] | ||
return height * width | ||
|
||
@staticmethod | ||
def calculate_normalized_histogram(image): | ||
number_of_bins = 256 | ||
channels_number = 3 # because of conversion from PIL to opencv | ||
histogram = cv2.calcHist([image], | ||
range(channels_number), | ||
None, | ||
[number_of_bins] * channels_number, | ||
[0, 256] * channels_number) | ||
cv2.normalize(histogram, histogram, 0, 256, cv2.NORM_MINMAX) | ||
return histogram | ||
|
||
@staticmethod | ||
def compare_histograms(image_a, image_b): | ||
histogram_a = MetricHistogramsCorrelation.calculate_normalized_histogram(image_a) | ||
histogram_b = MetricHistogramsCorrelation.calculate_normalized_histogram(image_b) | ||
result = cv2.compareHist(histogram_a, histogram_b, cv2.HISTCMP_CORREL) | ||
return result | ||
|
||
|
||
def run(): | ||
first_img = Image.open(sys.argv[1]) | ||
second_img = Image.open(sys.argv[2]) | ||
|
||
histograms_correlation_metric = MetricHistogramsCorrelation() | ||
|
||
print(histograms_correlation_metric.compute_metrics(first_img, second_img)) | ||
|
||
|
||
if __name__ == "__main__": | ||
run() |
Oops, something went wrong.