← Back to team overview

sikuli-driver team mailing list archive

[Question #682334]: sikuli onChange too sensitive

 

New question #682334 on Sikuli:
https://answers.launchpad.net/sikuli/+question/682334

I am trying to detect image changes using the onChange function.
I have a region over my laptop camera and the code appears to work.
When the camera is covered no changes are observed, which is correct.

However, when the camera is not covered the change event fires every 300ms even when no change appeared to occur.
I have tried various values for the pixel thresh hold, the observe scan rate and the minimum similarity,
Does anybody have suggestions on my approach or settings ?

Below is my complete code solution.

---
package com.images;

import java.awt.image.RenderedImage;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

import org.sikuli.basics.Settings;
import org.sikuli.script.FindFailed;
import org.sikuli.script.ImagePath;
import org.sikuli.script.Key;
import org.sikuli.script.Match;
import org.sikuli.script.ObserveEvent;
import org.sikuli.script.ObserverCallBack;
import org.sikuli.script.Pattern;
import org.sikuli.script.Region;
import org.sikuli.script.Screen;

public class ChangeDemo {

	private static final double OBSERVATION_DURATION = 5;
	private static final String IMAGE_PATH = "images";
	private LocalDateTime lastDateTime;
	private List<String> messages = new ArrayList<>();

	// Define camera region
	private static int X = 1115;
	private static int Y = 340;
	private static int H = 355;
	private static int W = 640;

	public static void main(String[] args) {
		new ChangeDemo().trackChanges();
	}

	public ChangeDemo() {
		Settings.UserLogs = true;
		Settings.UserLogTime = true;
		ImagePath.setBundlePath(IMAGE_PATH);
	}

	private void trackChanges() {
		if (!openCamera()) {
			return;
		}

		process();

		closeCamera();
	}

	private boolean openCamera() {
		boolean result = false;
		String imageFileName = "magnifier.png";

		Screen screen = new Screen();
		Match match = findTarget(screen, imageFileName);
		if (null == match)
			return false;

		try {
			match.click(imageFileName);
			match.wait(1.0);
			match.type("camera" + Key.ENTER);
			match.wait(3.0);
			result = true;
		} catch (FindFailed e) {
			log("** FindFailed: " + e.getMessage());
		}

		return result;
	}

	private void closeCamera() {
		Screen screen = new Screen();
		Match match = findTarget(screen, "camera-window-icons.png");
		if (null == match)
			return;

		match = findTarget(match, "camera-window-close.png");
		if (null == match)
			return;
		match.click();
	}

	private void process() {
		// The number of times actual search operations are performed per second
		float observeScanRate = 3f;

		// The minimum area size in pixels that changes it’s content to trigger a change
		// event
		// when using Region.onChange() when no value is specified.
		// The default value is 50 (a rectangle of about 7x7 Pixels).
		int pixelChanged = 50;

		// Each Pattern object has its own min similarity value, which is 0.7, if no
		// similarity is given.
		float minSimilarity = 0.7f;

		observe(observeScanRate, pixelChanged, minSimilarity);
		writeMessages(observeScanRate, pixelChanged, minSimilarity);
	}

	private void observe(float observeScanRate, int threshold, float minSimilarity) {
		Settings.MinSimilarity = minSimilarity;
		Settings.ObserveScanRate = observeScanRate;
		Settings.ObserveMinChangedPixels = threshold;
		log("Settings.MinSimilarity: " + Settings.MinSimilarity);
		log("Settings.ObserveScanRate: " + Settings.ObserveScanRate);
		log("Settings.ObserveMinChangedPixels: " + Settings.ObserveMinChangedPixels);

		lastDateTime = null;
		LocalDateTime observeStartDateTime = LocalDateTime.now();

		Region cameraRegion = new Screen().setRect(X, Y, W, H);
		cameraRegion.setObserveScanRate(observeScanRate);

		ObserverCallBack eventCallBack = new ObserverCallBack() {
			@Override
			public void changed(ObserveEvent e) {
				LocalDateTime dateTime = LocalDateTime.now();
				Match m = e.getMatch();
				if (null!=m) {
					//m.highlight(0.3f, "red");
					m.saveScreenCapture("changes", "m_"+e.getName()+"_"+e.getCount());
				}
				Region r = e.getRegion();
				if (null!=r) {
					//r.highlight(0.3f, "red");
					r.saveScreenCapture("changes", "r_"+e.getName()+"_"+e.getCount());
				}

				if (null != e.getImage()) {
					try {
						RenderedImage image = (RenderedImage) e.getImage();
						ImageIO.write(image, "jpg", new File("changes/"+e.getName() + ".jpg"));
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}

				log("** " + dateTime + " " + e);
				if (null != lastDateTime) {
					long seconds = lastDateTime.until(dateTime, ChronoUnit.SECONDS);
					long ms = lastDateTime.until(dateTime, ChronoUnit.MILLIS);
					log("\t lastDateTime " + lastDateTime + "  Seconds: " + seconds + "  ms: " + ms);
				}
				//highlightChanges(e);
				lastDateTime = dateTime;
			}

			private void highlightChanges(ObserveEvent e) {
				int sequence = 0;
				for (Match change : e.getChanges()) {
					if (null != change) {
						sequence++;
						String id = e.getCount() + "_" + e.getName() + "_" + sequence;
						log(id + " " + " match: " + change.getScore() + " " + change);
						change.saveScreenCapture("changes", id);
						// change.highlight(0.3f, "red");
					}
				}
			}
		};

		cameraRegion.onChange(threshold, eventCallBack);
		cameraRegion.observe(OBSERVATION_DURATION);
		cameraRegion.stopObserver();

		LocalDateTime observeEndDateTime = LocalDateTime.now();
		long minutes = observeStartDateTime.until(observeEndDateTime, ChronoUnit.MINUTES);
		long seconds = observeStartDateTime.until(observeEndDateTime, ChronoUnit.SECONDS);
		long ms = observeStartDateTime.until(observeEndDateTime, ChronoUnit.MILLIS);
		log("Observation StartDateTime: " + observeStartDateTime + " EndDateTime: " + observeEndDateTime + "  Minutes: "
				+ minutes + "  Seconds: " + seconds + "  ms: " + ms);
	}

	private void log(String msg) {
		System.out.println(msg);
		messages.add(msg + System.lineSeparator());
	}

	private Match findTarget(Region region, String imageFileName) {
		return findTarget(region, imageFileName, 0.7f);
	}

	private Match findTarget(Region region, String imageFileName, float similar) {
		System.out.println("findTarget() ImageFileName: " + imageFileName + " similar: " + similar);
		Match matchedResult = null;
		try {
			Pattern pattern = new Pattern(imageFileName).similar(similar);
			matchedResult = region.find(pattern);
		} catch (FindFailed e) {
			System.out.println("FindFailed : " + e.getMessage());
		}
		return matchedResult;
	}

	private void writeMessages(float observeScanRate, int pixelChanged, float minSimilarity) {
		FileWriter fileWriter = null;
		try {
			fileWriter = new FileWriter(
					"logs\\log_" + LocalDateTime.now().getNano() + "_OSR" + String.format("%.2f", observeScanRate)
							+ "_P" + pixelChanged + "_MS" + String.format("%.2f", minSimilarity) + ".txt");
			for (String message : messages) {
				fileWriter.write(message);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			messages.clear();
			try {
				if (fileWriter != null) {
					fileWriter.flush();
					fileWriter.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
}

-- 
You received this question notification because your team Sikuli Drivers
is an answer contact for Sikuli.