PageSourceSearch

https://www.toolpic.com/app/editor/libs/sift.js

js toolpic.com collected 2026-10-02 10:53:20 UTC 6,525 bytes, 183 lines download raw bytes

1/**
2 * SIFT: scale-invariant-feature-transform, keypoints
3 * 
4 * @author toolpic.com
5 */
6
7/* global HELPER, IMAGE, ImageFilters, LAYER, canvas_active */
8
9var SIFT = new SIFT_CLASS();
10
11function SIFT_CLASS() {
12	/**
13	 * contrast check, smaller - more points, better accuracy, but slower
14	 */
15	var avg_offset = 50;
16	
17	/**
18	 * how much pixels to check for each side to get average
19	 */
20	var avg_step = 4;
21
22	//generate key points for image
23	this.generate_keypoints = function (canvas, show_points) {
24		var W = canvas.width;
25		var H = canvas.height;
26		HELPER.timer_init();
27
28		//check whitespace
29		var trim_details = IMAGE.trim_info(canvas);
30		W = W - trim_details.left - trim_details.right;
31		H = H - trim_details.top - trim_details.bottom;
32		//make copy
33		var clone = document.createElement("canvas");
34		clone.width = W;
35		clone.height = H;
36		var ctx = clone.getContext("2d");
37		ctx.drawImage(canvas, -trim_details.left, -trim_details.top, canvas.width, canvas.height);
38
39		//greyscale
40		var imageData = ctx.getImageData(0, 0, W, H);
41		var filtered = ImageFilters.GrayScale(imageData); //add effect
42		ctx.putImageData(filtered, 0, 0);
43
44		//make few copies and blur each
45		var n = 5;
46		var copies = [];
47		for (var i = 0; i < n; i++) {
48			var tmp_canvas = document.createElement("canvas");
49			tmp_canvas.width = W;
50			tmp_canvas.height = H;
51			var ctx_i = tmp_canvas.getContext("2d");
52			ctx_i.drawImage(clone, 0, 0);
53
54			//gausian blur
55			var imageData = ctx_i.getImageData(0, 0, W, H);
56			var filtered = ImageFilters.GaussianBlur(imageData, i + 0.5); //add effect
57			ctx_i.putImageData(filtered, 0, 0);
58
59			copies.push(tmp_canvas);
60		}
61
62		//find extreme points
63		var points = [];
64		var n0 = avg_step * 2 + 1;
65		for (var c = 1; c < copies.length - 1; c++) {
66			var imageData = copies[c].getContext("2d").getImageData(0, 0, W, H).data;
67			var imageData0 = copies[c - 1].getContext("2d").getImageData(0, 0, W, H).data;
68			var imageData2 = copies[c + 1].getContext("2d").getImageData(0, 0, W, H).data;
69			for (var j = avg_step; j < H - avg_step; j++) {
70				for (var i = avg_step; i < W - avg_step; i++) {
71					var x = (i + j * W) * 4;
72					if (imageData[x + 3] == 0)
73						continue; //transparent
74					if (imageData[x] < imageData[x - 4] || imageData[x] < imageData[x + 4] || imageData[x] > imageData[x - 4] || imageData[x] > imageData[x + 4]) {
75						var x_pre = (i + (j - 1) * W) * 4;
76						var x_post = (i + (j + 1) * W) * 4;
77						//calc average
78						var area_average = 0;
79						for (var l = -avg_step; l <= avg_step; l++) {
80							var avgi = (i + (j - l) * W) * 4;
81							for (var a = -avg_step; a <= avg_step; a++) {
82								area_average += imageData[avgi + 4 * a];
83							}
84						}
85						area_average = area_average / (n0 * n0);
86						//max
87						if (imageData[x] + avg_offset < area_average) {
88							var min = Math.min(imageData[x_pre - 4], imageData[x_pre], imageData[x_pre + 4], imageData[x - 4], imageData[x + 4], imageData[x_post - 4], imageData[x_post], imageData[x_post + 4]);
89							if (imageData[x] <= min) {
90								var min0 = Math.min(imageData0[x_pre - 4], imageData0[x_pre], imageData0[x_pre + 4], imageData0[x - 4], imageData0[x + 4], imageData0[x_post - 4], imageData0[x_post], imageData0[x_post + 4]);
91								if (imageData[x] <= min0) {
92									var min2 = Math.min(imageData2[x_pre - 4], imageData2[x_pre], imageData2[x_pre + 4], imageData2[x - 4], imageData2[x + 4], imageData2[x_post - 4], imageData2[x_post], imageData2[x_post + 4]);
93									if (imageData[x] <= min2)
94										points.push({
95											x: i + trim_details.left,
96											y: j + trim_details.top,
97											w: Math.round(area_average - imageData[x] - avg_offset)
98										});
99								}
100							}
101							continue;
102						}
103						//min
104						if (imageData[x] - avg_offset > area_average) {
105							var max = Math.max(imageData[x_pre - 4], imageData[x_pre], imageData[x_pre + 4], imageData[x - 4], imageData[x + 4], imageData[x_post - 4], imageData[x_post], imageData[x_post + 4]);
106							if (imageData[x] >= max) {
107								var max0 = Math.max(imageData0[x_pre - 4], imageData0[x_pre], imageData0[x_pre + 4], imageData0[x - 4], imageData0[x + 4], imageData0[x_post - 4], imageData0[x_post], imageData0[x_post + 4]);
108								if (imageData[x] >= max0) {
109									var max2 = Math.max(imageData2[x_pre - 4], imageData2[x_pre], imageData2[x_pre + 4], imageData2[x - 4], imageData2[x + 4], imageData2[x_post - 4], imageData2[x_post], imageData2[x_post + 4]);
110									if (imageData[x] >= max2){
111										points.push({
112											x: i + trim_details.left,
113											y: j + trim_details.top,
114											w: Math.round(imageData[x] - area_average - avg_offset)
115										});
116									}
117								}
118							}
119						}
120					}
121				}
122			}
123		}
124		//make unique
125		for (var i = 0; i < points.length; i++) {
126			for (var j = 0; j < points.length; j++) {
127				if (i != j && points[i].x == points[j].x && points[i].y == points[j].y) {
128					points.splice(i, 1);
129					i--;
130					break;
131				}
132			}
133		}
134
135		//show points?
136		if (show_points === true) {
137			var time = HELPER.timer('', true);
138			console.log('key points: ' + points.length + ", " + time);
139			LAYER.layer_add();
140
141			var size = 3;
142			canvas_active().fillStyle = "#ff0000";
143			for (var i in points) {
144				var point = points[i];
145				canvas_active().beginPath();
146				canvas_active().rect(point.x - Math.floor(size / 2) + 1, point.y - Math.floor(size / 2) + 1, size, size);
147				canvas_active().fill();
148			}
149		}
150		else {
151			//sort by weights 
152			points.sort(function (a, b) {
153				return parseFloat(b.w) - parseFloat(a.w);
154			});
155			return {
156				points: points,
157				trim_details: trim_details
158			};
159		}
160	};
161	
162	//returns average value of requested area from greyscale image
163	//area = {x, y, w, h}
164	this.get_area_average = function (area, imageData, i, j, size) {
165		var imgData = imageData.data;
166		var sum = 0;
167		var n = 0;
168		size = size / 100; //prepare to use 1-100% values
169		var stop_x = i + Math.round(size * area.x) + Math.round(size * area.w);
170		var stop_y = j + Math.round(size * area.y) + Math.round(size * area.h);
171		var img_width4 = imageData.width * 4;
172		var k0, k;
173		for (var y = j + Math.round(size * area.y); y < stop_y; y++) {
174			k0 = y * img_width4;
175			for (var x = i + Math.round(size * area.x); x < stop_x; x++) {
176				k = k0 + (x * 4);
177				sum = sum + imgData[k];
178				n++;
179			}
180		}
181		return Math.round(sum / n);
182	};
183}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.