Commit 1d346e11 authored by Ari Trachtenberg's avatar Ari Trachtenberg
Browse files

public version

parent 8f2a0dac
Loading
Loading
Loading
Loading
+1.49 MiB

File added.

No diff preview for this file type.

+148 −92
Original line number Diff line number Diff line
# Group 6
# EC327 Final Project - Group 6 - Pixelshop



## Getting started

To make it easy for you to get started with GitLab, here's a list of recommended next steps.

Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!

## Add your files

- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:

```
cd existing_repo
git remote add origin https://agile.bu.edu/gitlab/ec327-public-projects/2024/group-6.git
git branch -M master
git push -uf origin master
```

## Integrate with your tools

- [ ] [Set up project integrations](https://agile.bu.edu/gitlab/ec327-public-projects/2024/group-6/-/settings/integrations)

## Collaborate with your team

- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)

## Test and Deploy

Use the built-in continuous integration in GitLab.

- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)

***

# Editing this README

When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.

## Suggestions for a good README

Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.

## Name
Choose a self-explaining name for your project.

## Description
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.

## Badges
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.

## Visuals
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.

## Installation
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.

## Usage
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.

## Support
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.

## Roadmap
If you have ideas for releases in the future, it is a good idea to list them in the README.

## Contributing
State if you are open to contributions and what your requirements are for accepting them.

For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.

You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.

## Authors and acknowledgment
Show your appreciation to those who have contributed to the project.

## License
For open source projects, say how it is licensed.

## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
## Summary
### Project Type
The project is an image filter run on a Web App.

### TL;DR
Users can upload an image in the specified format and manipulate it through resizing, recoloring, filtering, and fingerprinting.

### Description
Our image filter web app generates and manipulates images based on a text file that the user uploads. The file must follow a specific format: 
First row: the width of the image in pixels
Second row: the height of the image in pixels
Remaining lines: each line contains the rgb values (in the range 0-100) of each pixel separated by space, listed in column-major order

For example, with the file:<br>
2<br>
2<br>
100 0 0<br>
0 100 0<br>
100 0 100<br>
0 0 100<br>
A 2x2 image with a red pixel on the top left, green pixel on the bottom left, magenta pixel on the top right, and blue pixel on the bottom right would be generated.

Web app features:
- Resizing buttons (+/-) to scale the image by +0.1 or -0.1, or by a custom value entered in a text box.
- Filter buttons: Red-only, Green-only, Blue-only, Blur, and Grayscale.
- RGB Recoloring: Users can modify individual pixel colors by entering the RGB values of the original pixel color and the new color into text boxes.
- Users can add a custom fingerprint (entered in a text box) to the image and decode it later. The decoded fingerprint is displayed below this feature button.
- Reset button: Restores the image to its initial state.
- Displays the intensity distribution of the red, green, and blue components across all pixels in the image on a histogram.


## Authors
### Group members
JiaxingWang
KimberlyWong
OwenJiang
MimiPaule
Jueun Kang
BowenTan



### Roles
#### Role Table
Name | Project Lead | Front End | Back End 1 | Back End 2 | Documenter | Tester | Total
---|---|---|---|---|---|---|---
JiaxingWang | 40% | 10% | 0% | 10% | 20% | 20% | 100%
BowenTan | 10% | 0% | 50% | 0% | 20% | 20% | 100%
OwenJiang | 20% | 60% | 0% | 10% | 0% | 10% | 100%
MimiPaule | 10% | 20% | 50% | 0% | 10% | 10% | 100%
JueunKang | 0% | 0% | 0% | 60% | 20% | 20% | 100%
KimberlyWong | 20% | 10% | 0% | 20% | 30% | 20% | 100%
Total | 100% | 100% | 100% | 100% | 100% | 100% | 600%

#### Member Contributions

JiaxingWang: Led project planning, scheduling, and meetings. Integrated front-end and back-end while advising and optimizing front- and back-end features such as recolor and keepOneColor. Documented functions and created test files.

BowenTan: Developed various back-end features such as blurring, recolor and keepOneColor of Pixelshop. Documented functions and files. Created test cases for script validation. 

OwenJiang: Established formatting and styling for the front-end website. Established the interfacing of image data with back end. Helped lead front-end and project meetings. Assisted with various back-end function development. Created Python scripts to generate .txt files for testing.

MimiPaule: Developed back-end features such as grayscale, reset, and scaling. Drafted initial front-end design. Assisted with preparing project deliverables.

JueunKang: Developed back-end features such as initial file validation, input processing, file downloading, and fingerprinting. Documented and tested functions. Documented usage directions of Pixelshop.

KimberlyWong: Assisted with front-end formatting. Created front-end display as well as back-end calculations for histogram. Created and developed project demo presentation.

---
## Accomplishments

### Minimum requirements
#### Completed
1. Display image on the screen
2. Zero out green and blue components of the pixels

#### Partially completed
We believe that no minimum required features were partially completed.

### Possible features
#### Completed
1. Grow/shrink the image by a user-specified factor. [5%]
2. Recolor any one color in the image with a different one. [5%]
3. Histogram of colors present in pixels. [10%]
4. Blurring the image. [10%]
5. Convert the image to grayscale. [10%]
6. Encode and decoding fingerprint string. [20%]

#### Partially completed
We believe that no additional features were partially completed.

---
## Execution
All sourced code needed to compile and run Pixelshop from scratch is present in the PROJECT-REPORT branch.

### Installation
1. In the PROJECT-REPORT branch, click on "Code" (often a blue button) to download the source code as a .zip.
2. Extract the downloaded compressed folder.
3. In the extracted folder, open index.html.

### Usage
1. Click the "Choose File" button.
2. Upload a .txt file that satisfies the requirements. Example .txt files, some of which will work, and some of which will intentionally fail, are inside test_files folder.
3. Apply features! <br>
    - +/- : Enlarges/shrinks image by +0.1 or -0.1 <br>
    - Scale: Enlarges/shrinks image by the inputted value (value needs to be greater than 0) <br>
        - Enter the value then press the "Apply" button <br>
    - Original Size: Sets the output image size equal to the resolution of the image as determined by the width and height (first 2 lines) of the .txt file.
        - Very useful for large images to eliminate artifacting.
    - Red only: Sets green and blue values to zero (making the image red) <br>
    - Green only: Sets red and blue values to zero (making the image green) <br>
    - Blue only: Sets red and green values to zero (making the image blue) <br>
    - Blur: Makes the image slightly blurrier <br>
    - Grayscale: Sets the RGB values of each pixel as the average of all three RGB values of that pixel, effectively grayscaling the image <br>
    - Recolor: Recolor any color in the image with a different one.
        - Enter the color you want to change on the left text box and the color that will replace it on the left text box then press the "Recolor" button. 
    - Fingerprint: Encodes a fingerprint inside the image, which can then be downloaded as well as decoded <br>
        - Enter the fingerprint string then press the "Add Fingerprint" <br>
        - When successful, the encoded image file can be downloaded.
        - Use the "Decode Fingerprint" feature to see the encoded fingerprint. 
            - The image on screen can be immediately decoded. The downloaded file can also be uploaded and decoded. 

---
## Miscellaneous

### Extra features
- Reset button on website for user to easily reset all filters.
- Allow the user to also zero out red and blue, and red and green.
    - Overall, this means that the user can make the image completely red, completely blue, or completely green.
- There is a mouse event listener when hovering over the uploaded image that provides the user with the pixel color (from 0 to 100) of the image.
    - This will help them recolor the image.

### Challenges
- Integration of different functions into the website was a headache. We could not import/export files as modules because doing so required the website to run off a server.
    - To solve this issue, we used a few global variables to keep our scripts running smoothly. We recognize that global variables are discouraged in programming, so we kept this to a minimum.
- The histogram and fingerprinting features are more advanced, and thus their development was challenging in ensuring the desired behavior.
- The initial image display used a <\div> element for each pixel, meaning that larger pixels were disproportionately affected by live browser lag (as a 600x600 image would have 360,000 elements). To fix this, we instead used the <\canvas> element so that there is always one element for any image size.
    - Advantage: Users can right click and save their filtered images as .png!
    - Disadvantage: This transition caused artifacting when scaling large images.
    - Workaround: Using the "Original Size" button, users can see large images in the original resolution, thus eliminating any artifacts.

### Supporting material
- Test files can be found in the test_files folder. Note: some files intentionally do not meet the file requirements of Pixelshop.
- [Google Slides](https://docs.google.com/presentation/d/1PtEN3oQqJG76A7jr3C4sQ8y-Gjkyc1ff37i0khznVmc/edit?usp=sharing) 

### Release
This project may be visible to the broader public for the benefit of all team members. For example, team members may showcase this project to potential employers. Verbal consensus reached on November 25, 2024.
 No newline at end of file

functions/blurring.js

0 → 100644
+74 −0
Original line number Diff line number Diff line
/**
 * Main contributor to this file:
 * @author BowenTan
 */

let blurButton = document.getElementById("blur");

/**
 * calculates the neighbors and fill in the blurring arrays
 * @param {integer} width the width of the image
 * @param {integer} height the height of the image
 * @param {Array} rArray red values for each pixel
 * @param {Array} gArray green values for each pixel
 * @param {Array} bArray blue values for each pixel
 * @returns the following:
        a. rBlurred : An array of the blurred red values
        b. gBlurred : An array of the blurred green values
        c. bBlurred: An array of the blurred blue values
*/
function blurImage(width, height, rArray, gArray, bArray) {
    const rBlurred = new Array(rArray.length).fill(0);
    const gBlurred = new Array(gArray.length).fill(0);
    const bBlurred = new Array(bArray.length).fill(0);

    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            const index = y * width + x;

            // collect neighbors
            const neighbors = [
                index, // the current pixel
                y > 0 ? (y - 1) * width + x : null, // the pixel above
                y < height - 1 ? (y + 1) * width + x : null, // the pixel below
                x > 0 ? y * width + (x - 1) : null, // the pixel to the left
                x < width - 1 ? y * width + (x + 1) : null // the pixel to the right
            ]
                .filter(i => i !== null);

            // average each color channel
            const rSum = neighbors.reduce((sum, i) => sum + rArray[i], 0);
            const gSum = neighbors.reduce((sum, i) => sum + gArray[i], 0);
            const bSum = neighbors.reduce((sum, i) => sum + bArray[i], 0);

            const count = neighbors.length; //get the total number of neighbors for each pixel

            // fill the blurred RGB arrays. Rounding down.
            rBlurred[index] = Math.floor(rSum / count);
            gBlurred[index] = Math.floor(gSum / count);
            bBlurred[index] = Math.floor(bSum / count);
        }
    }
    return [ rBlurred, gBlurred, bBlurred ];
}

/**
 * Event listener that calls the function and returns the blurred image when "Blur" button is clicked.
*/
blurButton.addEventListener("click", async function () {
    try {
        // apply the blur effect
        [imageData.rArray, imageData.gArray, imageData.bArray] = blurImage(imageData.width, imageData.height, imageData.rArray, imageData.gArray, imageData.bArray);

        // refresh the image display
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        drawRgbGrid(imageData);

        //update histogram with blur values
        createHistogram(imageData.rArray, imageData.gArray, imageData.bArray);
    } catch (e) {
        notificationText.textContent = `${this.textContent}: Please import an image first.`;
        notificationText.style.color = "red";
    }

});
+18 −0
Original line number Diff line number Diff line
// Main contributor: JiaxingWang

let colorInfo = document.getElementById("currentColor");
let imageArea = document.getElementById("image");

// Full disclosure: This event listener was initially written by ChatGPT, then adapted by JiaxingWang for Pixelshop.
imageArea.addEventListener('mousemove', (e) => {
    try {
        // Try-catch makes sure color values will only write when there is an image.
        if (!imageData) throw Error;
        const [r, g, b] = ctx.getImageData(e.offsetX, e.offsetY, 1, 1).data;
        // Normalized to display from 0 to 100
        const [rNorm, gNorm, bNorm] = [Math.floor(r / 2.55), Math.floor(g / 2.55), Math.floor(b / 2.55)];
        colorInfo.textContent = `Color: ${rNorm} ${gNorm} ${bNorm}`;
        colorInfo.style.color = ((rNorm+gNorm+bNorm)/3 < 50) ? "white" : "black"; // if average > 50, make black, otherwise white
        colorInfo.style.background = `rgb(${r}, ${g}, ${b})`;
    } catch (e) {}
});
 No newline at end of file

functions/download.js

0 → 100644
+47 −0
Original line number Diff line number Diff line
/** Main contributor:
 * @author JueunKang 
 */

/**
 * Write the fingerprinted image into a string output.
 * @param {array} rArray red values of each pixel
 * @param {array} gArray green values of each pixel
 * @param {array} bArray blue values of each pixel
 * @param {int} height The height of the image (in pixels)
 * @param {int} width The width of the image (in pixels)
 * @returns A string with the r g b values in each line corresponding to a pixel. 
 */
function reorderString(rArray, gArray, bArray, height, width){
  let outputFile = "";
  outputFile += `${width}\n`;
  outputFile += `${height}\n`;
  for (let x = 0; x < width; x++){
    for (let y = 0; y < height; y++){
      const pixelLine = y * width + x;
      outputFile += `${rArray[pixelLine]} ${gArray[pixelLine]} ${bArray[pixelLine]}\n`;
    }
  }
  return outputFile;
}

/**
 * Downloads a .txt file with width, height, and RGB values.
 * @param {array} rArray red values of each pixel
 * @param {array} gArray green values of each pixel
 * @param {array} bArray blue values of each pixel
 * @param {int} width The width of the image (in pixels)
 * @param {int} height The height of the image (in pixels)
 */
function download(rArray, bArray, gArray, width, height) {
  const outputFile = reorderString(rArray, bArray, gArray, height, width);
  const blob = new Blob([outputFile], { type: 'text/plain' });

  const tempLink = document.createElement('a');
  const url = URL.createObjectURL(blob);
  tempLink.href = url;
  tempLink.setAttribute('download', 'encoded_fingerprint_file.txt');

  document.body.appendChild(tempLink);
  tempLink.click();
  document.body.removeChild(tempLink);
}
 No newline at end of file
Loading