_computeOrientationHistograms(prunedExtremasT, pyramidImagesT) {
		const oneOver2PI = 0.159154943091895;

		const gaussianImagesT = [];
		for (let i = 1; i < pyramidImagesT.length; i++) {
			gaussianImagesT.push(pyramidImagesT[i][1]);
		}

		if (!this.tensorCaches.orientationHistograms) {
			tf.tidy(() => {
				const gwScale = -1.0 / (2 * ORIENTATION_GAUSSIAN_EXPANSION_FACTOR * ORIENTATION_GAUSSIAN_EXPANSION_FACTOR);
				const radius = ORIENTATION_GAUSSIAN_EXPANSION_FACTOR * ORIENTATION_REGION_EXPANSION_FACTOR;
				const radiusCeil = Math.ceil(radius);

				const radialProperties = [];
				for (let y = -radiusCeil; y <= radiusCeil; y++) {
					for (let x = -radiusCeil; x <= radiusCeil; x++) {
						const distanceSquare = x * x + y * y;

						// may just assign w = 1 will do, this could be over complicated.
						if (distanceSquare <= radius * radius) {
							const _x = distanceSquare * gwScale;
							// fast expontenial approx
							let w = (720 + _x * (720 + _x * (360 + _x * (120 + _x * (30 + _x * (6 + _x)))))) * 0.0013888888;
							radialProperties.push([y, x, w]);
						}
					}
				}

				this.tensorCaches.orientationHistograms = {
					radialPropertiesT: tf.keep(tf.tensor(radialProperties, [radialProperties.length, 3])),
				}
			});
		}
		const { radialPropertiesT } = this.tensorCaches.orientationHistograms;

		/* if (!this.kernelCaches.computeOrientationHistograms) {
			const imageVariableNames = [];
			for (let i = 1; i < pyramidImagesT.length; i++) {
				imageVariableNames.push('image' + i);
			}

			let kernel1SubCodes = `float getPixel(int octave, int y, int x) {`;
			for (let i = 1; i < pyramidImagesT.length; i++) {
				kernel1SubCodes += `
	  if (octave == ${i}) {
		return getImage${i}(y, x);
	  }
	`
			}
			kernel1SubCodes += `}`;

			const kernel1 = {
				variableNames: [...imageVariableNames, 'extrema', 'radial'],
				outputShape: [prunedExtremasT.shape[0], radialPropertiesT.shape[0], 2], // last dimension: [fbin, magnitude]
				userCode: `
	  ${kernel1SubCodes}

	  void main() {
		ivec3 coords = getOutputCoords();
		int featureIndex = coords[0];
		int radialIndex = coords[1];
		int propertyIndex = coords[2];

		int radialY = int(getRadial(radialIndex, 0));
		int radialX = int(getRadial(radialIndex, 1));
		float radialW = getRadial(radialIndex, 2);

		int octave = int(getExtrema(featureIndex, 1));
		int y = int(getExtrema(featureIndex, 2));
		int x = int(getExtrema(featureIndex, 3));

		int xp = x + radialX;
		int yp = y + radialY;

		float dy = getPixel(octave, yp+1, xp) - getPixel(octave, yp-1, xp);
		float dx = getPixel(octave, yp, xp+1) - getPixel(octave, yp, xp-1);

		if (propertyIndex == 0) {
		  // be careful that atan(0, 0) gives 1.57 instead of 0 (different from js), but doesn't matter here, coz magnitude is 0
		  
		  float angle = atan(dy, dx) + ${Math.PI};
		  float fbin = angle * ${ORIENTATION_NUM_BINS}. * ${oneOver2PI};
		  setOutput(fbin);
		  return;
		}

		if (propertyIndex == 1) {
		  float mag = sqrt(dx * dx + dy * dy);
		  float magnitude = radialW * mag;
		  setOutput(magnitude);
		  return;
		}
	  }

	`
			}

			const kernel2 = {
				variableNames: ['fbinMag'],
				outputShape: [prunedExtremasT.shape[0], ORIENTATION_NUM_BINS],
				userCode: `
	  void main() {
		ivec2 coords = getOutputCoords();
		int featureIndex = coords[0];
		int binIndex = coords[1];

		float sum = 0.;
		for (int i = 0; i < ${radialPropertiesT.shape[0]}; i++) {
		  float fbin = getFbinMag(featureIndex, i, 0);
		  int bin = int(floor(fbin - 0.5));
		  int b1 = imod(bin + ${ORIENTATION_NUM_BINS}, ${ORIENTATION_NUM_BINS});
		  int b2 = imod(bin + 1 + ${ORIENTATION_NUM_BINS}, ${ORIENTATION_NUM_BINS});

		  if (b1 == binIndex || b2 == binIndex) {
		float magnitude = getFbinMag(featureIndex, i, 1);
		float w2 = fbin - float(bin) - 0.5;
		float w1 = w2 * -1. + 1.;

		if (b1 == binIndex) {
		  sum += w1 * magnitude;
		}
		if (b2 == binIndex) {
		  sum += w2 * magnitude;
		}
		  }
		}
		setOutput(sum);
	  }
	`
			}

			this.kernelCaches.computeOrientationHistograms = [kernel1, kernel2];
		} */

		return tf.tidy(() => {
			/* const [program1, program2] = this.kernelCaches.computeOrientationHistograms;
			const result1 = this._compileAndRun(program1, [...gaussianImagesT, prunedExtremasT, radialPropertiesT]);
			const result2 = this._compileAndRun(program2, [result1]); 
			return result2;*/
			return tf.engine().runKernel('ComputeOrientationHistograms', { gaussianImagesT, prunedExtremasT, radialPropertiesT, pyramidImagesLength: pyramidImagesT.length });
		});
	}
@param {tf.Tensor<tf.Rank>} prunedExtremasT 
@param {tf.Tensor<tf.Rank>[]} pyramidImagesT 
@returns