mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-06-04 11:14:21 +08:00
Merge pull request #5765 from Ultimaker/log_litho
Logarithmic lithophane thickness
This commit is contained in:
commit
1cdad60b5b
@ -143,6 +143,52 @@ UM.Dialog
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UM.TooltipArea {
|
||||||
|
Layout.fillWidth:true
|
||||||
|
height: childrenRect.height
|
||||||
|
text: catalog.i18nc("@info:tooltip","For lithophanes a simple logarithmic model for translucency is available. For height maps the pixel values correspond to heights linearly.")
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Color Model"
|
||||||
|
width: 150 * screenScaleFactor
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
ComboBox {
|
||||||
|
id: color_model
|
||||||
|
objectName: "ColorModel"
|
||||||
|
model: [ catalog.i18nc("@item:inlistbox","Linear"), catalog.i18nc("@item:inlistbox","Translucency") ]
|
||||||
|
width: 180 * screenScaleFactor
|
||||||
|
onCurrentIndexChanged: { manager.onColorModelChanged(currentIndex) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UM.TooltipArea {
|
||||||
|
Layout.fillWidth:true
|
||||||
|
height: childrenRect.height
|
||||||
|
text: catalog.i18nc("@info:tooltip","The percentage of light penetrating a print with a thickness of 1 millimeter. Lowering this value increases the contrast in dark regions and decreases the contrast in light regions of the image.")
|
||||||
|
visible: color_model.currentText == catalog.i18nc("@item:inlistbox","Translucency")
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: catalog.i18nc("@action:label", "1mm Transmittance (%)")
|
||||||
|
width: 150 * screenScaleFactor
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: transmittance
|
||||||
|
objectName: "Transmittance"
|
||||||
|
focus: true
|
||||||
|
validator: RegExpValidator {regExp: /^[1-9]\d{0,2}([\,|\.]\d*)?$/}
|
||||||
|
width: 180 * screenScaleFactor
|
||||||
|
onTextChanged: { manager.onTransmittanceChanged(text) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UM.TooltipArea {
|
UM.TooltipArea {
|
||||||
Layout.fillWidth:true
|
Layout.fillWidth:true
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
from PyQt5.QtGui import QImage, qRed, qGreen, qBlue
|
from PyQt5.QtGui import QImage, qRed, qGreen, qBlue
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
|
|
||||||
@ -46,9 +48,9 @@ class ImageReader(MeshReader):
|
|||||||
|
|
||||||
def _read(self, file_name):
|
def _read(self, file_name):
|
||||||
size = max(self._ui.getWidth(), self._ui.getDepth())
|
size = max(self._ui.getWidth(), self._ui.getDepth())
|
||||||
return self._generateSceneNode(file_name, size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512, self._ui.lighter_is_higher)
|
return self._generateSceneNode(file_name, size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512, self._ui.lighter_is_higher, self._ui.use_transparency_model, self._ui.transmittance_1mm)
|
||||||
|
|
||||||
def _generateSceneNode(self, file_name, xz_size, peak_height, base_height, blur_iterations, max_size, lighter_is_higher):
|
def _generateSceneNode(self, file_name, xz_size, peak_height, base_height, blur_iterations, max_size, lighter_is_higher, use_transparency_model, transmittance_1mm):
|
||||||
scene_node = SceneNode()
|
scene_node = SceneNode()
|
||||||
|
|
||||||
mesh = MeshBuilder()
|
mesh = MeshBuilder()
|
||||||
@ -99,12 +101,14 @@ class ImageReader(MeshReader):
|
|||||||
for x in range(0, width):
|
for x in range(0, width):
|
||||||
for y in range(0, height):
|
for y in range(0, height):
|
||||||
qrgb = img.pixel(x, y)
|
qrgb = img.pixel(x, y)
|
||||||
avg = float(qRed(qrgb) + qGreen(qrgb) + qBlue(qrgb)) / (3 * 255)
|
if use_transparency_model:
|
||||||
height_data[y, x] = avg
|
height_data[y, x] = (0.299 * math.pow(qRed(qrgb) / 255.0, 2.2) + 0.587 * math.pow(qGreen(qrgb) / 255.0, 2.2) + 0.114 * math.pow(qBlue(qrgb) / 255.0, 2.2))
|
||||||
|
else:
|
||||||
|
height_data[y, x] = (0.212655 * qRed(qrgb) + 0.715158 * qGreen(qrgb) + 0.072187 * qBlue(qrgb)) / 255 # fast computation ignoring gamma and degamma
|
||||||
|
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
|
||||||
if not lighter_is_higher:
|
if lighter_is_higher == use_transparency_model:
|
||||||
height_data = 1 - height_data
|
height_data = 1 - height_data
|
||||||
|
|
||||||
for _ in range(0, blur_iterations):
|
for _ in range(0, blur_iterations):
|
||||||
@ -124,6 +128,13 @@ class ImageReader(MeshReader):
|
|||||||
|
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
|
||||||
|
if use_transparency_model:
|
||||||
|
divisor = 1.0 / math.log(transmittance_1mm / 100.0) # log-base doesn't matter here. Precompute this value for faster computation of each pixel.
|
||||||
|
min_luminance = (transmittance_1mm / 100.0) ** (peak_height - base_height)
|
||||||
|
for (y, x) in numpy.ndindex(height_data.shape):
|
||||||
|
mapped_luminance = min_luminance + (1.0 - min_luminance) * height_data[y, x]
|
||||||
|
height_data[y, x] = base_height + divisor * math.log(mapped_luminance) # use same base as a couple lines above this
|
||||||
|
else:
|
||||||
height_data *= scale_vector.y
|
height_data *= scale_vector.y
|
||||||
height_data += base_height
|
height_data += base_height
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@ class ImageReaderUI(QObject):
|
|||||||
self.peak_height = 2.5
|
self.peak_height = 2.5
|
||||||
self.smoothing = 1
|
self.smoothing = 1
|
||||||
self.lighter_is_higher = False;
|
self.lighter_is_higher = False;
|
||||||
|
self.use_transparency_model = True;
|
||||||
|
self.transmittance_1mm = 50.0; # based on pearl PLA
|
||||||
|
|
||||||
self._ui_lock = threading.Lock()
|
self._ui_lock = threading.Lock()
|
||||||
self._cancelled = False
|
self._cancelled = False
|
||||||
@ -75,6 +77,7 @@ class ImageReaderUI(QObject):
|
|||||||
|
|
||||||
self._ui_view.findChild(QObject, "Base_Height").setProperty("text", str(self.base_height))
|
self._ui_view.findChild(QObject, "Base_Height").setProperty("text", str(self.base_height))
|
||||||
self._ui_view.findChild(QObject, "Peak_Height").setProperty("text", str(self.peak_height))
|
self._ui_view.findChild(QObject, "Peak_Height").setProperty("text", str(self.peak_height))
|
||||||
|
self._ui_view.findChild(QObject, "Transmittance").setProperty("text", str(self.transmittance_1mm))
|
||||||
self._ui_view.findChild(QObject, "Smoothing").setProperty("value", self.smoothing)
|
self._ui_view.findChild(QObject, "Smoothing").setProperty("value", self.smoothing)
|
||||||
|
|
||||||
def _createConfigUI(self):
|
def _createConfigUI(self):
|
||||||
@ -144,3 +147,11 @@ class ImageReaderUI(QObject):
|
|||||||
@pyqtSlot(int)
|
@pyqtSlot(int)
|
||||||
def onImageColorInvertChanged(self, value):
|
def onImageColorInvertChanged(self, value):
|
||||||
self.lighter_is_higher = (value == 1)
|
self.lighter_is_higher = (value == 1)
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def onColorModelChanged(self, value):
|
||||||
|
self.use_transparency_model = (value == 0)
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def onTransmittanceChanged(self, value):
|
||||||
|
self.transmittance_1mm = value
|
||||||
|
Loading…
x
Reference in New Issue
Block a user