Фатальная ошибка и необычный результат при преобразовании MAT OpenCV в изображение JavafxJAVA

Программисты JAVA общаются здесь
Ответить
Anonymous
 Фатальная ошибка и необычный результат при преобразовании MAT OpenCV в изображение Javafx

Сообщение Anonymous »

Это последующий вопрос к двум предыдущим сообщениям о наиболее эффективном способе преобразования объекта 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 затем преобразуется в изображение, которое будет отображаться в приложении. />
  • Код: Выделить всё

    ProcessImage
    - это создание пользовательского интерфейса и его элементов.

Код: Выделить всё

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");

// Create column constraints
ColumnConstraints mainColumn = new ColumnConstraints();
mainColumn.setHgrow(javafx.scene.layout.Priority.ALWAYS);
mainGridPane.getColumnConstraints().add(mainColumn);

// Create row constraints
RowConstraints row1 = new RowConstraints();
row1.setVgrow(javafx.scene.layout.Priority.NEVER);
RowConstraints row2 = new RowConstraints();
row2.setVgrow(javafx.scene.layout.Priority.ALWAYS);
mainGridPane.getRowConstraints().addAll(row1, row2);

// Create first HBox (controls section)
HBox controlsHBox = new HBox();
controlsHBox.setAlignment(javafx.geometry.Pos.CENTER);

// Create inner GridPane for controls
GridPane controlsGridPane = new GridPane();
controlsGridPane.setAlignment(javafx.geometry.Pos.CENTER);
controlsGridPane.setVgap(20);
controlsGridPane.setStyle("-fx-background-color: #00ff93");
HBox.setHgrow(controlsGridPane, javafx.scene.layout.Priority.ALWAYS);

// Create column constraint for inner GridPane
ColumnConstraints innerColumn = new ColumnConstraints();
innerColumn.setHgrow(javafx.scene.layout.Priority.ALWAYS);
controlsGridPane.getColumnConstraints().add(innerColumn);

// Create slider controls
createSliderControl(controlsGridPane, 0, "Min H", "minHueSlider", "minHueLabel");
createSliderControl(controlsGridPane, 1, "Max H", "maxHueSlider", "maxHueLabel");
createSliderControl(controlsGridPane, 2, "Min S", "minSaturationSlider", "minSaturationLabel");
createSliderControl(controlsGridPane, 3, "Max S", "maxSaturationSlider", "maxSaturationLabel");
createSliderControl(controlsGridPane, 4, "Min V", "minValueSlider", "minValueLabel");
createSliderControl(controlsGridPane, 5, "Max V", "maxValueSlider", "maxValueLabel");

controlsHBox.getChildren().add(controlsGridPane);
mainGridPane.add(controlsHBox, 0, 0);

// Create second HBox (image section)
HBox imageHBox = new HBox();
imageHBox.setAlignment(javafx.geometry.Pos.CENTER);

// Create ImageView
ImageView inputImage = new ImageView();
inputImage.setId("inputImage");
inputImage.setPickOnBounds(true);
inputImage.setPreserveRatio(true);
inputImage.setFitHeight(500);
inputImage.setFitWidth(500);

// Load image
try {
Image image = new Image(getClass().getResource("path_to_your_image").toString(), true);
inputImage.setImage(image);
} catch (Exception e) {
System.err.println("Could not load image: "  + e.getMessage());
}

imageHBox.getChildren().add(inputImage);
mainGridPane.add(imageHBox, 0, 1);

return mainGridPane;
}

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 slider
Slider slider = new Slider();
slider.setId(sliderId);
slider.setBlockIncrement(0.1);
slider.setMajorTickUnit(0.5);
slider.setMax(255.0);
slider.getStyleClass().add("slider");
HBox.setHgrow(slider, javafx.scene.layout.Priority.ALWAYS);

// 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));

hbox.getChildren().addAll(nameLabel, slider, valueLabel);
gridPane.add(hbox, 0, rowIndex);
}

@Override
public void start(Stage stage) throws Exception {
GridPane parentNode = createFilterWindow();
Scene scene = new Scene(parentNode, 900, 700);
stage.setTitle("Test window");

ProcessImageController controller = new ProcessImageController();
controller.mainGridPane = parentNode;
controller.inputImage = (ImageView) parentNode.lookup("#inputImage");
controller.setInputImagePath("C:\\Users\\PC\\Desktop\\bird-9445431.jpg");
controller.minHueLabel = (Label) parentNode.lookup("#minHueLabel");
controller.maxHueLabel = (Label) scene.lookup("#maxHueLabel");
controller.minSaturationLabel = (Label) parentNode.lookup("#minSaturationLabel");
controller.maxSaturationLabel = (Label) parentNode.lookup("#maxSaturationLabel");
controller.minValueLabel = (Label) parentNode.lookup("#minValueLabel");
controller.maxValueLabel = (Label) parentNode.lookup("#maxValueLabel");
controller.minHueSlider = (Slider) scene.lookup("#minHueSlider");
controller.maxHueSlider = (Slider) scene.lookup("#maxHueSlider");
controller.minSaturationSlider = (Slider) parentNode.lookup("#minSaturationSlider");
controller.maxSaturationSlider = (Slider) parentNode.lookup("#maxSaturationSlider");
controller.minValueSlider = (Slider) parentNode.lookup("#minValueSlider");
controller.maxValueSlider = (Slider) parentNode.lookup("#maxValueSlider");
controller.initialize();

try {
stage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResourceAsStream("resources/window-icon.png"))));
} catch (Exception e) {
System.out.println(e.getMessage());
}

stage.setScene(scene);
stage.show();

stage.show();
}
< /code>
[list]
[*]ProcessImageController
является контроллером представления и его управления.
[/list]

Код: Выделить всё

import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import org.opencv.core.Mat;
import tasks.MatToFXImage;
import utils.FilterByHSV;
import utils.HSVAdaptor;

import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

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 static String stripFilePrefix(String filePath) {
if (filePath != null && filePath.startsWith("file:")) {
return filePath.substring("file:".length());
}
return filePath;
}

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);
}

@FXML
public void setSlidersValues(double minHue, double maxHue, double minSaturation, double maxSaturation, double minValue, double maxValue) {
minHueSlider.setValue(minHue);
maxHueSlider.setValue(maxHue);
minSaturationSlider.setValue(minSaturation);
maxSaturationSlider.setValue(maxSaturation);
minValueSlider.setValue(minValue);
maxValueSlider.setValue(maxValue);
scheduleDebouncedUpdate();
}

public void dispose() {
ScheduledFuture pending = pendingDebounce.getAndSet(null);
if (pending != null) pending.cancel(false);

debounceExecutor.shutdown();
imageProcessingExecutor.shutdown();

try {
if (!debounceExecutor.awaitTermination(500, TimeUnit.MILLISECONDS)) {
debounceExecutor.shutdownNow();
}
if (!imageProcessingExecutor.awaitTermination(1, TimeUnit.SECONDS)) {
imageProcessingExecutor.shutdownNow();
}
} catch (InterruptedException e) {
debounceExecutor.shutdownNow();
imageProcessingExecutor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
< /code>
[list]
[*] MatToFXImage
это задача, при которой коврик будет преобразован в изображение Javafx.

Код: Выделить всё

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.WritableRaster;
import java.io.ByteArrayInputStream;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.Arrays;

import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;
import javafx.scene.image.PixelBuffer;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.WritableImage;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.imgcodecs.Imgcodecs;

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);

return new WritableImage(pixels);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

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()));

// Fatal Error
// try {
//     System.out.println(mat.dataAddr());
//     return fromAddress(mat.dataAddr(), mat.width(), mat.height());
// }catch (Exception e) {
//         e.printStackTrace();
//         return null;
// }

// Strange result 1
// try {
//     int size = (int) (mat.total() * mat.channels());
//     byte[] byteArray = new byte[size * 5];
//     mat.get(0, 0, byteArray);

//     ByteBuffer buffer = ByteBuffer.wrap(byteArray);
//     PixelFormat pixelFormat = PixelFormat.getByteBgraPreInstance();

//     return fromBuffer(buffer, mat.width(), mat.height(), pixelFormat);
// } catch (Exception e) {
//     e.printStackTrace();
//     return null;
// }

// Strange result 2
// try {
//     int size = (int) (mat.total() * mat.channels());
//     System.out.println(mat.channels());
//     byte[] byteArray = new byte[size * 5];

//     mat.get(0, 0, byteArray);

//     ByteBuffer buffer = ByteBuffer.wrap(byteArray);
//     PixelFormat pixelFormat = PixelFormat.getByteBgraPreInstance();
//     PixelBuffer  pixelBuffer = new PixelBuffer(mat.width(), mat.height(), buffer, pixelFormat);

//     return new WritableImage(pixelBuffer);
// } catch (Exception e) {
//     e.printStackTrace();
//     return null;
// }

// Works fine
// int type;
// type = determineImageType(mat.type());

// BufferedImage image = new BufferedImage(mat.width(), mat.height(), type);
// WritableRaster raster = image.getRaster();
// DataBufferByte dataBuffer = (DataBufferByte) raster.getDataBuffer();

// // byte[] data = dataBuffer.getData();

// mat.get(0, 0, dataBuffer.getData());
// return SwingFXUtils.toFXImage(image, null);
}
}

< /code>
< /li>
 FilterByHSV
сохраняет только пиксели в указанном диапазоне и превращает остальные в серой.

Код: Выделить всё

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 в их реальные значения.

Код: Выделить всё

public class HSVAdaptor {
public static double adaptHue(double hueValue, double from, double to) {
return (hueValue / from) * to;
}

public static double adaptSaturation(double saturation) {
return saturation * 255;
}

public static double adaptValue(double value) {
return value * 255;
}
}
[/list]
Теперь это должен выглядеть результат (рабочие части комментируются как // нормально )


Подробнее здесь: https://stackoverflow.com/questions/797 ... vafx-image
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «JAVA»