Это последующий вопрос к двум предыдущим сообщениям о наиболее эффективном способе преобразования объекта OpenCV Mat в изображение Javafx (post 1, post 2). < /p>
@james_d, @slaw, указано на два возможных подхода, в которых @slaw разработано в его ответе: < /p>
matse -из Matse). ByteBuffer -> PixelBuffer -> вернуть изображение
с использованием wriseableimage < /p>
< /blockquote>
и < /p>
byte [] -> bytebuffer -> Прочитайте буфер с использованием riseabletimage и
return the image> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p>. /> Для первого я получаю фатальную ошибку. Между тем, со вторым подходом, я получаю необычный результат, когда изображение становится повторяющимся на оси x, отображая горизонтальные полосы. Filterbyhsv имеет Filterimage (int minhue, int maxhue, int minsaturation, int maxsaturation, int minvalue, int maxvalue) Метод, который возвращает матрицу, где все пиксели в указанном диапазоне HSV, с использованием Sliders, отображаются по цвету, а остальные - серые. SLIDER , Filterimage вызывается, и полученный объект MAT затем преобразуется в изображение, которое будет отображаться в приложении. />
package utils;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
public class FilterByHSV {
private static final int COLOR_CONVERSION_BGR_TO_HSV = Imgproc.COLOR_BGR2HSV;
private static final int COLOR_CONVERSION_BGR_TO_GRAY = Imgproc.COLOR_BGR2GRAY;
private static final int COLOR_CONVERSION_GRAY_TO_BGR = Imgproc.COLOR_GRAY2BGR;
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
Mat image;
Mat imageInHSV;
Mat grey;
Mat mask;
Mat result;
public FilterByHSV(String imageToFilterPath) {
imageInHSV = new Mat();
grey = new Mat();
mask = new Mat();
result = new Mat();
image = Imgcodecs.imread(imageToFilterPath);
Imgproc.cvtColor(image, imageInHSV, COLOR_CONVERSION_BGR_TO_HSV);
Imgproc.cvtColor(image, grey, COLOR_CONVERSION_BGR_TO_GRAY);
}
public Mat filterImage(int minHue, int maxHue, int minSaturation, int maxSaturation, int minValue, int maxValue) {
Core.inRange(imageInHSV, new Scalar(minHue, minSaturation, minValue), new Scalar(maxHue, maxSaturation, maxValue), mask);
Imgproc.cvtColor(grey, result, COLOR_CONVERSION_GRAY_TO_BGR);
image.copyTo(result, mask);
return result;
}
}
< /code>
< /li>
HSVAdaptor
преобразовать значения HSV в их реальные значения.
Это последующий вопрос к двум предыдущим сообщениям о наиболее эффективном способе преобразования объекта OpenCV Mat в изображение Javafx (post 1, post 2). < /p> @james_d, @slaw, указано на два возможных подхода, в которых @slaw разработано в его ответе: < /p>
matse -из Matse). ByteBuffer -> PixelBuffer -> вернуть изображение с использованием wriseableimage < /p> < /blockquote> и < /p>
byte [] -> bytebuffer -> Прочитайте буфер с использованием riseabletimage и return the image> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p>. /> Для первого я получаю фатальную ошибку. Между тем, со вторым подходом, я получаю необычный результат, когда изображение становится повторяющимся на оси x, отображая горизонтальные полосы. Filterbyhsv имеет Filterimage (int minhue, int maxhue, int minsaturation, int maxsaturation, int minvalue, int maxvalue) Метод, который возвращает матрицу, где все пиксели в указанном диапазоне HSV, с использованием Sliders, отображаются по цвету, а остальные - серые. SLIDER , Filterimage вызывается, и полученный объект MAT затем преобразуется в изображение, которое будет отображаться в приложении. /> [list] [*][code]ProcessImage[/code] - это создание пользовательского интерфейса и его элементов. [/list] [code]import javafx.application.Application; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.Slider; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.RowConstraints; import javafx.scene.text.Font; import javafx.stage.Stage;
import java.util.Objects;
public class ProcessImage extends Application {
private GridPane createFilterWindow() { // Create main GridPane GridPane mainGridPane = new GridPane(); mainGridPane.setPrefHeight(600.0); mainGridPane.setPrefWidth(800.0); mainGridPane.setStyle("-fx-background-color: #5b5b5b");
private void createSliderControl(GridPane gridPane, int rowIndex, String labelText, String sliderId, String labelId) { HBox hbox = new HBox(); hbox.setAlignment(javafx.geometry.Pos.CENTER); hbox.setPrefHeight(60);
// Create label Label nameLabel = new Label(labelText); nameLabel.setTextFill(javafx.scene.paint.Color.BLACK); nameLabel.setFont(Font.font("Times New Roman Bold", 15.0)); HBox.setMargin(nameLabel, new Insets(0, 20, 0, 20));
// Create value label Label valueLabel = new Label("0"); valueLabel.setId(labelId); valueLabel.setTextFill(javafx.scene.paint.Color.BLACK); valueLabel.setFont(Font.font("Times New Roman Bold", 15.0)); HBox.setMargin(valueLabel, new Insets(0, 20, 0, 20));
public class ProcessImageController { // UI Components @FXML public GridPane mainGridPane; @FXML public ImageView inputImage;
// Labels @FXML public Label minHueLabel; @FXML public Label maxHueLabel; @FXML public Label minSaturationLabel; @FXML public Label maxSaturationLabel; @FXML public Label minValueLabel; @FXML public Label maxValueLabel;
// Sliders @FXML public Slider minHueSlider; @FXML public Slider maxHueSlider; @FXML public Slider minSaturationSlider; @FXML public Slider maxSaturationSlider; @FXML public Slider minValueSlider; @FXML public Slider maxValueSlider;
private FilterByHSV filterByHSV;
// Single-thread executor ensures processing tasks don't overlap and preserves order. private final ExecutorService imageProcessingExecutor = Executors.newSingleThreadExecutor(r -> { Thread t = new Thread(r, "image-processing"); t.setDaemon(true); return t; });
// Scheduled executor used only for debounce timing. private final ScheduledExecutorService debounceExecutor = Executors.newSingleThreadScheduledExecutor(r -> { Thread t = new Thread(r, "debounce-timer"); t.setDaemon(true); return t; });
// Holds the currently scheduled debounce task so it can be cancelled/rescheduled. private final AtomicReference prev = pendingDebounce.getAndSet(debounceExecutor.schedule(() -> Platform.runLater(this::updateImageFilter), DEBOUNCE_MS, TimeUnit.MILLISECONDS)); if (prev != null) prev.cancel(false); }
private Image matToImage(Mat mat) { try { MatToFXImage task = new MatToFXImage(mat); // task.exceptionProperty().subscribe(Throwable::printStackTrace); new Thread(task).start(); return task.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException("Failed to convert Mat to Image", e); } }
private void updateImageFilter() { if (filterByHSV == null) return;
// Read current slider values once int minHue = (int) HSVAdaptor.adaptHue(minHueSlider.getValue(), 255, 179); int maxHue = (int) HSVAdaptor.adaptHue(maxHueSlider.getValue(), 255, 179);
int minSaturation = (int) minSaturationSlider.getValue(); int maxSaturation = (int) maxSaturationSlider.getValue();
int minValue = (int) minValueSlider.getValue(); int maxValue = (int) maxValueSlider.getValue();
imageProcessingExecutor.submit(() -> { try { Mat outputMat = filterByHSV.filterImage(minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue); Image outputImage = matToImage(outputMat); Platform.runLater(() -> inputImage.setImage(outputImage)); } catch (Exception e) { // Consider logging to a logger instead of throwing if UI stability is preferred throw new RuntimeException("Image processing failed", e); } }); }
@FXML public void setInputImagePath(String path) { Objects.requireNonNull(path, "path"); Image image = new Image("file:" + path); inputImage.setImage(image); String filePath = stripFilePrefix(image.getUrl()); filterByHSV = new FilterByHSV(filePath); }
public class MatToFXImage extends Task { private final Mat mat;
public MatToFXImage(Mat mat) { if (mat == null) throw new IllegalArgumentException("Mat object can't be null");
this.mat = mat; }
private int determineImageType(int matType) { if (matType == CvType.CV_8UC1) return BufferedImage.TYPE_BYTE_GRAY; else if (matType == CvType.CV_8UC3) return BufferedImage.TYPE_3BYTE_BGR; else throw new IllegalArgumentException("Unsupported Mat type: " + matType); }
public Image fromAddress(long address, int width, int height) { try { int length = width * height * 6; // may want to guard against overflow var segment = MemorySegment.ofAddress(address).reinterpret(length);
var buffer = segment.asByteBuffer(); var format = PixelFormat.getByteBgraPreInstance(); var pixels = new PixelBuffer(width, height, buffer, format);
public Image fromBuffer(T buffer, int width, int height, PixelFormat format) { var image = new WritableImage(width, height); var writer = image.getPixelWriter(); writer.setPixels(0, 0, width, height, format, buffer, width);
return image; }
@Override protected Image call() { // Works fine MatOfByte byteMat = new MatOfByte(); Imgcodecs.imencode(".bmp", mat, byteMat); return new Image(new ByteArrayInputStream(byteMat.toArray()));
public class FilterByHSV { private static final int COLOR_CONVERSION_BGR_TO_HSV = Imgproc.COLOR_BGR2HSV; private static final int COLOR_CONVERSION_BGR_TO_GRAY = Imgproc.COLOR_BGR2GRAY; private static final int COLOR_CONVERSION_GRAY_TO_BGR = Imgproc.COLOR_GRAY2BGR;
public Mat filterImage(int minHue, int maxHue, int minSaturation, int maxSaturation, int minValue, int maxValue) { Core.inRange(imageInHSV, new Scalar(minHue, minSaturation, minValue), new Scalar(maxHue, maxSaturation, maxValue), mask); Imgproc.cvtColor(grey, result, COLOR_CONVERSION_GRAY_TO_BGR); image.copyTo(result, mask);
return result; } }
< /code> < /li> HSVAdaptor[/code] преобразовать значения HSV в их реальные значения. [code]public class HSVAdaptor { public static double adaptHue(double hueValue, double from, double to) { return (hueValue / from) * to; }