// File: kebwi[P]Trifid/src/ImageProcs.cpp
// Function: addSensorNoiseWithAutoISO(ImageReal&, bool, NoiseParam)
void addSensorNoiseWithAutoISO(ImageReal &grayImage, bool applyBayer, NoiseParam noiseParams)
{
	cout << "Adding grayscale sensor noise with autoISO..." << endl;
	
	//Check to see if the image is normalized
	int totalPixels = grayImage.totalPixels();
	Float_t maxVal = 0;
	for (int i = 0; i < totalPixels; i++)
	{
		assert(grayImage[i] >= 0 && grayImage[i] <= 1.0);
		if (grayImage[i] > maxVal)
			maxVal = grayImage[i];
	}
	if (maxVal != 1.0)
		cout << "  ***  NOTE  ***  This input image is not normalized yet" << endl;
	
	cout << "Performing auto-ISO search for proper amplifier gain to achieve good saturation..." << endl;
	
	//Perform "auto-ISO" by altering the amplifier gain until image saturation is achieved which matches the input image.
	//This is not how a digital camera would perform auto-ISO (there is no "input image").
	//A real camera would aim for some rational indication of good saturation,
	//e.g., a mean pixel value of X some fraction of 1.0, or perhaps some ratio Y of clipped pixels to total pixels, something like that.
	//Using the input image as the target saturation ensures that all output images have the same saturation and that they correspond to the input image,
	//which then enables subsequent comparisons across a set of images (PSNR, Lab Delta E, etc.).
	Float_t nonOutlierMeanPixelValOri = calcNonoutlierMeanPixelValue(grayImage, 3);
	cout << "  Input image mean intensity (auto-gain target intensity): " << nonOutlierMeanPixelValOri << endl << endl;
	ImageReal grayImageOri = grayImage;
	Float_t maxPPFD = luxToPPFD(noiseParams.lux_, 0, noiseParams.focalRatio_, noiseParams.pixelPitchMicrons_, noiseParams.fullWellCapacity_);
	noiseParams.ampGain_ = noiseParams.fullWellCapacity_ / (maxPPFD * noiseParams.exposureSecs_);
	Float_t stepSize = noiseParams.ampGain_ * .5;	//Beginning of binary search (well, approximately a binary search at any rate)
	vector<pair<Float_t, Float_t> > ampGainHistory;
	Float_t nonOutlierMeanPixelVal = 0;
	int lastStepDir = 0;
	while (true)
	{
		cout << endl << "--------------------------------------------------------------------------------" << endl;
		
		int historyIdx = 0;
		for (historyIdx = 0; historyIdx < ampGainHistory.size(); historyIdx++)
			if (ampGainHistory[historyIdx].first == noiseParams.ampGain_)
				break;
		
		int numSaturatedPixels = totalPixels;
		if (historyIdx == ampGainHistory.size())
		{
			//Restore the original image
			grayImage = grayImageOri;
			
			//Noisify the image
			cout << endl << setw(40) << left << "  Noisifying with ags: " << noiseParams.ampGain_ << endl;
			addSensorNoise(grayImage, applyBayer, noiseParams);
			
			//Determine the saturation
			nonOutlierMeanPixelVal = calcNonoutlierMeanPixelValue(grayImage, 3);
			cout << endl << "    nonOutlierMeanPixelVal: " << nonOutlierMeanPixelVal << endl;
		}
		else	//This amp gain has already been tried
		{
			maxVal = ampGainHistory[historyIdx].second;
			cout << "  Already tried amp gain " << noiseParams.ampGain_ << ".  maxVal = " << maxVal << endl;
		}
		
		if (nonOutlierMeanPixelVal >= nonOutlierMeanPixelValOri - kAutoGainConvergence
			&& nonOutlierMeanPixelVal <= nonOutlierMeanPixelValOri + kAutoGainConvergence)	//Acceptable saturation
		{
			assert(historyIdx == ampGainHistory.size());
			cout << "    Good saturation achieved" << endl;
			break;
		}
		
		if (stepSize < .001)
		{
			cout << "Bailing on binary search, step size is too small." << endl;
			break;
		}
		
		ampGainHistory.push_back(pair<Float_t, Float_t>(noiseParams.ampGain_, maxVal));
		
		//cout << "    lastStepDir:" << setw(21) << lastStepDir << endl;
		if (nonOutlierMeanPixelVal < nonOutlierMeanPixelValOri - kAutoGainConvergence)	//Increase gain
		{
			if (lastStepDir == -1)
				stepSize *= .5;
			//cout << "    Increasing ags from " << setw(13) << ampGainScalar;
			noiseParams.ampGain_ += stepSize;
			lastStepDir = 1;
			//cout << " to " << setw(10) << ampGainScalar << " with ss " << setw(10) << stepSize << endl;
		}
		else	//Decrease gain
		{
			if (lastStepDir == 1)
				stepSize *= .5;
			//cout << "    Decreasing ags from " << setw(13) << ampGainScalar;
			noiseParams.ampGain_ -= stepSize;
			if (noiseParams.ampGain_ < 1)
			{
				noiseParams.ampGain_ = 1;
				stepSize *= .5;
				//cout << "    Increasing ags from " << setw(13) << ampGainScalar;
				noiseParams.ampGain_ += stepSize;
				lastStepDir = 1;
			}
			lastStepDir = -1;
			//cout << " to " << setw(10) << ampGainScalar << " with ss " << setw(10) << stepSize << endl;
		}
		//cout << "    lastStepDir:" << setw(21) << lastStepDir << endl;
		
		assert(noiseParams.ampGain_ >= 1);//0);
	}
	
	cout << "Final ags: " << noiseParams.ampGain_ << endl;
	/*
	ofstream ofs;
	ofs.open("noiseData.txt");
	for (int i = 0; i < 256; i++)
		ofs << i << '\t' << sMeanNoise[i] << '\t' << sMeanNoiseProporation[i] << '\t'
			<< sMeanOutputValue[i] << '\t' << sMeanError[i] << endl;
	ofs.close();
	*/
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x280, %rsp            # imm = 0x280
movq %rdx, -0x190(%rbp)
movb %sil, %al
movq %rdi, -0x8(%rbp)
andb $0x1, %al
movb %al, -0x9(%rbp)
movq %rdx, -0x18(%rbp)
movq 0x37457(%rip), %rdi     # 0x59fd0
leaq 0x28b21(%rip), %rsi     # 0x4b6a1
callq 0x4390
movq %rax, %rdi
movq 0x37429(%rip), %rsi     # 0x59fb8
callq 0x43d0
movq -0x8(%rbp), %rdi
callq 0xf200
movl %eax, -0x1c(%rbp)
xorps %xmm0, %xmm0
movsd %xmm0, -0x28(%rbp)
movl $0x0, -0x2c(%rbp)
movl -0x2c(%rbp), %eax
cmpl -0x1c(%rbp), %eax
jge 0x22c66
movq -0x8(%rbp), %rdi
movl -0x2c(%rbp), %esi
callq 0xef70
movsd (%rax), %xmm0
xorl %eax, %eax
xorps %xmm1, %xmm1
ucomisd %xmm1, %xmm0
movb %al, -0x191(%rbp)
jb 0x22bfd
movq -0x8(%rbp), %rdi
movl -0x2c(%rbp), %esi
callq 0xef70
movsd 0x26430(%rip), %xmm0    # 0x49020
ucomisd (%rax), %xmm0
setae %al
movb %al, -0x191(%rbp)
movb -0x191(%rbp), %al
testb $0x1, %al
jne 0x22c09
jmp 0x22c0b
jmp 0x22c2a
leaq 0x28abd(%rip), %rdi     # 0x4b6cf
leaq 0x28118(%rip), %rsi     # 0x4ad31
movl $0x3f9, %edx            # imm = 0x3F9
leaq 0x28ad3(%rip), %rcx     # 0x4b6f8
callq 0x4240
movq -0x8(%rbp), %rdi
movl -0x2c(%rbp), %esi
callq 0xef70
movsd (%rax), %xmm0
ucomisd -0x28(%rbp), %xmm0
jbe 0x22c56
movq -0x8(%rbp), %rdi
movl -0x2c(%rbp), %esi
callq 0xef70
movsd (%rax), %xmm0
movsd %xmm0, -0x28(%rbp)
jmp 0x22c58
movl -0x2c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x2c(%rbp)
jmp 0x22baf
movsd -0x28(%rbp), %xmm0
movsd 0x263ad(%rip), %xmm1    # 0x49020
ucomisd %xmm1, %xmm0
jne 0x22c7d
jp 0x22c7d
jmp 0x22c9f
movq 0x3734c(%rip), %rdi     # 0x59fd0
leaq 0x28aab(%rip), %rsi     # 0x4b736
callq 0x4390
movq %rax, %rdi
movq 0x3731e(%rip), %rsi     # 0x59fb8
callq 0x43d0
movq 0x3732a(%rip), %rdi     # 0x59fd0
movq %rdi, -0x1b0(%rbp)
leaq 0x28abb(%rip), %rsi     # 0x4b76f
callq 0x4390
movq %rax, %rdi
movq 0x372f5(%rip), %rsi     # 0x59fb8
movq %rsi, -0x1a8(%rbp)
callq 0x43d0
movq -0x8(%rbp), %rdi
movsd 0x2803d(%rip), %xmm0    # 0x4ad18
callq 0x1faf0
movq -0x1b0(%rbp), %rdi
movsd %xmm0, -0x38(%rbp)
leaq 0x28acf(%rip), %rsi     # 0x4b7c2
callq 0x4390
movq %rax, %rdi
movsd -0x38(%rbp), %xmm0
callq 0x4670
movq -0x1a8(%rbp), %rsi
movq %rax, %rdi
callq 0x43d0
movq -0x1a8(%rbp), %rsi
movq %rax, %rdi
callq 0x43d0
movq -0x8(%rbp), %rsi
leaq -0x78(%rbp), %rdi
callq 0x15620
movq -0x190(%rbp), %rax
movsd 0xa8(%rax), %xmm2
movsd 0x20(%rax), %xmm0
movsd 0x40(%rax), %xmm3
movq 0x48(%rax), %xmm4
movaps 0x262fb(%rip), %xmm1    # 0x49050
punpckldq %xmm1, %xmm4    # xmm4 = xmm4[0],xmm1[0],xmm4[1],xmm1[1]
movapd 0x262ff(%rip), %xmm1    # 0x49060
subpd %xmm1, %xmm4
movaps %xmm4, %xmm1
unpckhpd %xmm4, %xmm4            # xmm4 = xmm4[1,1]
addsd %xmm1, %xmm4
xorps %xmm1, %xmm1
callq 0x1ff20
movsd %xmm0, -0x1a0(%rbp)
jmp 0x22d82
movq -0x190(%rbp), %rax
movsd -0x1a0(%rbp), %xmm0
movsd %xmm0, -0x80(%rbp)
movq 0x48(%rax), %xmm0
movaps 0x262ae(%rip), %xmm1    # 0x49050
punpckldq %xmm1, %xmm0    # xmm0 = xmm0[0],xmm1[0],xmm0[1],xmm1[1]
movapd 0x262b2(%rip), %xmm1    # 0x49060
subpd %xmm1, %xmm0
movaps %xmm0, %xmm1
unpckhpd %xmm0, %xmm0            # xmm0 = xmm0[1,1]
addsd %xmm1, %xmm0
movsd -0x80(%rbp), %xmm1
mulsd 0x98(%rax), %xmm1
divsd %xmm1, %xmm0
movsd %xmm0, 0xa0(%rax)
movsd 0x26232(%rip), %xmm0    # 0x49010
mulsd 0xa0(%rax), %xmm0
movsd %xmm0, -0x98(%rbp)
leaq -0xb0(%rbp), %rdi
callq 0x25a20
xorps %xmm0, %xmm0
movsd %xmm0, -0xb8(%rbp)
movl $0x0, -0xbc(%rbp)
jmp 0x22e11
movq 0x371b8(%rip), %rdi     # 0x59fd0
movq 0x37199(%rip), %rsi     # 0x59fb8
callq 0x43d0
movq %rax, -0x1b8(%rbp)
jmp 0x22e2d
movq -0x1b8(%rbp), %rdi
leaq 0x289c3(%rip), %rsi     # 0x4b7fe
callq 0x4390
movq %rax, -0x1c0(%rbp)
jmp 0x22e49
movq -0x1c0(%rbp), %rdi
movq 0x37161(%rip), %rsi     # 0x59fb8
callq 0x43d0
jmp 0x22e5e
movl $0x0, -0xc0(%rbp)
movl $0x0, -0xc0(%rbp)
movslq -0xc0(%rbp), %rax
movq %rax, -0x1c8(%rbp)
leaq -0xb0(%rbp), %rdi
callq 0x25a40
movq %rax, %rcx
movq -0x1c8(%rbp), %rax
cmpq %rcx, %rax
jae 0x22f0e
movslq -0xc0(%rbp), %rsi
leaq -0xb0(%rbp), %rdi
callq 0x25a60
movq %rax, %rcx
movq -0x190(%rbp), %rax
movsd (%rcx), %xmm0
ucomisd 0xa0(%rax), %xmm0
jne 0x22ef8
jp 0x22ef8
jmp 0x22f0e
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x88(%rbp)
movl %eax, -0x8c(%rbp)
jmp 0x234f6
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x88(%rbp)
movl %eax, -0x8c(%rbp)
jmp 0x234ea
jmp 0x22efa
movl -0xc0(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xc0(%rbp)
jmp 0x22e72
movl -0x1c(%rbp), %eax
movl %eax, -0xc4(%rbp)
movslq -0xc0(%rbp), %rax
movq %rax, -0x1d0(%rbp)
leaq -0xb0(%rbp), %rdi
callq 0x25a40
movq %rax, %rcx
movq -0x1d0(%rbp), %rax
cmpq %rcx, %rax
jne 0x2312c
movq -0x8(%rbp), %rdi
leaq -0x78(%rbp), %rsi
callq 0x15690
jmp 0x22f53
movq 0x37076(%rip), %rdi     # 0x59fd0
movq 0x37057(%rip), %rsi     # 0x59fb8
callq 0x43d0
movq %rax, -0x1d8(%rbp)
jmp 0x22f6f
movl $0x28, %edi
callq 0xe410
movl %eax, -0x1dc(%rbp)
jmp 0x22f81
movq -0x1d8(%rbp), %rdi
movl -0x1dc(%rbp), %eax
movl %eax, -0xc8(%rbp)
movl -0xc8(%rbp), %esi
callq 0x4430
movq %rax, -0x1e8(%rbp)
jmp 0x22fa8
movq -0x1e8(%rbp), %rdi
leaq -0x14b86(%rip), %rsi    # 0xe430
callq 0x4110
movq %rax, -0x1f0(%rbp)
jmp 0x22fc4
movq -0x1f0(%rbp), %rdi
leaq 0x2887d(%rip), %rsi     # 0x4b84f
callq 0x4390
movq %rax, -0x1f8(%rbp)
jmp 0x22fe0
movq -0x1f8(%rbp), %rdi
movq -0x190(%rbp), %rax
movsd 0xa0(%rax), %xmm0
callq 0x4670
movq %rax, -0x200(%rbp)
jmp 0x23004
movq -0x200(%rbp), %rdi
movq 0x36fa6(%rip), %rsi     # 0x59fb8
callq 0x43d0
jmp 0x23019
movq -0x190(%rbp), %rsi
movq -0x8(%rbp), %rax
movq %rax, -0x210(%rbp)
movb -0x9(%rbp), %al
movb %al, -0x201(%rbp)
leaq -0x178(%rbp), %rdi
callq 0xec90
jmp 0x23042
movq -0x210(%rbp), %rdi
movb -0x201(%rbp), %al
movzbl %al, %esi
andl $0x1, %esi
leaq -0x178(%rbp), %rdx
callq 0x203e0
jmp 0x23063
leaq -0x178(%rbp), %rdi
callq 0xe3d0
movq -0x8(%rbp), %rdi
movsd 0x27c9d(%rip), %xmm0    # 0x4ad18
callq 0x1faf0
movsd %xmm0, -0x218(%rbp)
jmp 0x2308a
movsd -0x218(%rbp), %xmm0
movsd %xmm0, -0xb8(%rbp)
movq 0x36f2f(%rip), %rdi     # 0x59fd0
movq 0x36f10(%rip), %rsi     # 0x59fb8
callq 0x43d0
movq %rax, -0x220(%rbp)
jmp 0x230b6
movq -0x220(%rbp), %rdi
leaq 0x287a3(%rip), %rsi     # 0x4b867
callq 0x4390
movq %rax, -0x228(%rbp)
jmp 0x230d2
movq -0x228(%rbp), %rdi
movsd -0xb8(%rbp), %xmm0
callq 0x4670
movq %rax, -0x230(%rbp)
jmp 0x230ef
movq -0x230(%rbp), %rdi
movq 0x36ebb(%rip), %rsi     # 0x59fb8
callq 0x43d0
jmp 0x23104
jmp 0x231d6
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x88(%rbp)
movl %eax, -0x8c(%rbp)
leaq -0x178(%rbp), %rdi
callq 0xe3d0
jmp 0x234ea
movslq -0xc0(%rbp), %rsi
leaq -0xb0(%rbp), %rdi
callq 0x25a60
movsd 0x8(%rax), %xmm0
movsd %xmm0, -0x28(%rbp)
movq 0x36e80(%rip), %rdi     # 0x59fd0
leaq 0x2872d(%rip), %rsi     # 0x4b884
callq 0x4390
movq %rax, -0x238(%rbp)
jmp 0x23165
movq -0x238(%rbp), %rdi
movq -0x190(%rbp), %rax
movsd 0xa0(%rax), %xmm0
callq 0x4670
movq %rax, -0x240(%rbp)
jmp 0x23189
movq -0x240(%rbp), %rdi
leaq 0x28707(%rip), %rsi     # 0x4b89e
callq 0x4390
movq %rax, -0x248(%rbp)
jmp 0x231a5
movq -0x248(%rbp), %rdi
movsd -0x28(%rbp), %xmm0
callq 0x4670
movq %rax, -0x250(%rbp)
jmp 0x231bf
movq -0x250(%rbp), %rdi
movq 0x36deb(%rip), %rsi     # 0x59fb8
callq 0x43d0
jmp 0x231d4
jmp 0x231d6
movsd -0xb8(%rbp), %xmm0
movsd -0x38(%rbp), %xmm1
movsd 0x27aa5(%rip), %xmm2    # 0x4ac90
subsd %xmm2, %xmm1
ucomisd %xmm1, %xmm0
jb 0x23298
movsd -0xb8(%rbp), %xmm1
movsd 0x27a87(%rip), %xmm0    # 0x4ac90
addsd -0x38(%rbp), %xmm0
ucomisd %xmm1, %xmm0
jb 0x23298
movslq -0xc0(%rbp), %rax
movq %rax, -0x258(%rbp)
leaq -0xb0(%rbp), %rdi
callq 0x25a40
movq %rax, %rcx
movq -0x258(%rbp), %rax
cmpq %rcx, %rax
jne 0x23243
jmp 0x23262
leaq 0x28661(%rip), %rdi     # 0x4b8ab
leaq 0x27ae0(%rip), %rsi     # 0x4ad31
movl $0x431, %edx            # imm = 0x431
leaq 0x2849b(%rip), %rcx     # 0x4b6f8
callq 0x4240
movq 0x36d67(%rip), %rdi     # 0x59fd0
leaq 0x2865f(%rip), %rsi     # 0x4b8cf
callq 0x4390
movq %rax, -0x260(%rbp)
jmp 0x2327e
movq -0x260(%rbp), %rdi
movq 0x36d2c(%rip), %rsi     # 0x59fb8
callq 0x43d0
jmp 0x23293
jmp 0x23477
movsd 0x279f0(%rip), %xmm0    # 0x4ac90
ucomisd -0x98(%rbp), %xmm0
jbe 0x232e0
movq 0x36d1f(%rip), %rdi     # 0x59fd0
leaq 0x28634(%rip), %rsi     # 0x4b8ec
callq 0x4390
movq %rax, -0x268(%rbp)
jmp 0x232c6
movq -0x268(%rbp), %rdi
movq 0x36ce4(%rip), %rsi     # 0x59fb8
callq 0x43d0
jmp 0x232db
jmp 0x23477
movq -0x190(%rbp), %rsi
addq $0xa0, %rsi
leaq -0x188(%rbp), %rdi
leaq -0x28(%rbp), %rdx
callq 0x25ab0
jmp 0x23300
leaq -0xb0(%rbp), %rdi
leaq -0x188(%rbp), %rsi
callq 0x25a80
jmp 0x23315
movsd -0xb8(%rbp), %xmm1
movsd -0x38(%rbp), %xmm0
movsd 0x27966(%rip), %xmm2    # 0x4ac90
subsd %xmm2, %xmm0
ucomisd %xmm1, %xmm0
jbe 0x23383
cmpl $-0x1, -0xbc(%rbp)
jne 0x23355
movsd 0x25ccb(%rip), %xmm0    # 0x49010
mulsd -0x98(%rbp), %xmm0
movsd %xmm0, -0x98(%rbp)
movq -0x190(%rbp), %rax
movsd -0x98(%rbp), %xmm0
addsd 0xa0(%rax), %xmm0
movsd %xmm0, 0xa0(%rax)
movl $0x1, -0xbc(%rbp)
jmp 0x23434
cmpl $0x1, -0xbc(%rbp)
jne 0x233a4
movsd 0x25c7c(%rip), %xmm0    # 0x49010
mulsd -0x98(%rbp), %xmm0
movsd %xmm0, -0x98(%rbp)
movq -0x190(%rbp), %rax
movsd -0x98(%rbp), %xmm1
movsd 0xa0(%rax), %xmm0
subsd %xmm1, %xmm0
movsd %xmm0, 0xa0(%rax)
movsd 0x25c51(%rip), %xmm0    # 0x49020
ucomisd 0xa0(%rax), %xmm0
jbe 0x2342a
movq -0x190(%rbp), %rax
movsd 0x25c38(%rip), %xmm0    # 0x49020
movsd %xmm0, 0xa0(%rax)
movsd 0x25c18(%rip), %xmm0    # 0x49010
mulsd -0x98(%rbp), %xmm0
movsd %xmm0, -0x98(%rbp)
movsd -0x98(%rbp), %xmm0
addsd 0xa0(%rax), %xmm0
movsd %xmm0, 0xa0(%rax)
movl $0x1, -0xbc(%rbp)
movl $0xffffffff, -0xbc(%rbp) # imm = 0xFFFFFFFF
movq -0x190(%rbp), %rax
movsd 0xa0(%rax), %xmm0
movsd 0x25bd5(%rip), %xmm1    # 0x49020
ucomisd %xmm1, %xmm0
jb 0x23453
jmp 0x23472
leaq 0x284c4(%rip), %rdi     # 0x4b91e
leaq 0x278d0(%rip), %rsi     # 0x4ad31
movl $0x45b, %edx            # imm = 0x45B
leaq 0x2828b(%rip), %rcx     # 0x4b6f8
callq 0x4240
jmp 0x22e0f
movq 0x36b52(%rip), %rdi     # 0x59fd0
leaq 0x284b3(%rip), %rsi     # 0x4b938
callq 0x4390
movq %rax, -0x270(%rbp)
jmp 0x23493
movq -0x270(%rbp), %rdi
movq -0x190(%rbp), %rax
movsd 0xa0(%rax), %xmm0
callq 0x4670
movq %rax, -0x278(%rbp)
jmp 0x234b7
movq -0x278(%rbp), %rdi
movq 0x36af3(%rip), %rsi     # 0x59fb8
callq 0x43d0
jmp 0x234cc
leaq -0xb0(%rbp), %rdi
callq 0x25ae0
leaq -0x78(%rbp), %rdi
callq 0x157e0
addq $0x280, %rsp            # imm = 0x280
popq %rbp
retq
leaq -0xb0(%rbp), %rdi
callq 0x25ae0
leaq -0x78(%rbp), %rdi
callq 0x157e0
movq -0x88(%rbp), %rdi
callq 0x4620
nopl (%rax,%rax)